Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: PyCapsule_GetDestructor is allowed to return a nullptr destructor #4221

Merged
merged 4 commits into from
Oct 7, 2022

Conversation

galv
Copy link
Contributor

@galv galv commented Oct 7, 2022

Description

Previously, this code would error out if the destructor happened to be a nullptr. This is incorrect. nullptrs are allowed for capsule destructors.

"It is legal for a capsule to have a NULL destructor. This makes a NULL return code somewhat ambiguous; use PyCapsule_IsValid() or PyErr_Occurred() to disambiguate."

See:

https://docs.python.org/3/c-api/capsule.html#c.PyCapsule_GetDestructor

I noticed this while working on a type caster related to #3858 DLPack happens to allow the destructor not to be defined on a capsule, and I encountered such a case. See:

https://github.com/dmlc/dlpack/blob/e2bdd3bee8cb6501558042633fa59144cc8b7f5f/include/dlpack/dlpack.h#L219

Suggested changelog entry:

* Allow pybind11::capsule constructor to take null destructor pointers.

Previously, this code would error out if the destructor happened to be
a nullptr. This is incorrect. nullptrs are allowed for capsule
destructors.

"It is legal for a capsule to have a NULL destructor. This makes a
NULL return code somewhat ambiguous; use PyCapsule_IsValid() or
PyErr_Occurred() to disambiguate."

See:

https://docs.python.org/3/c-api/capsule.html#c.PyCapsule_GetDestructor

I noticed this while working on a type caster related to pybind#3858 DLPack
happens to allow the destructor not to be defined on a capsule, and I
encountered such a case. See:

https://github.com/dmlc/dlpack/blob/e2bdd3bee8cb6501558042633fa59144cc8b7f5f/include/dlpack/dlpack.h#L219
@Skylion007
Copy link
Collaborator

Thanks @galv, oversight on my part when rewriting the error handling of capsules a few months ago.

We also have an open issue to include dlpack casters as officially supported in pybind11 so consider making a PR once you finish writing the casters.

Copy link
Collaborator

@rwgk rwgk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix! (Sorry I missed this, too.)

@rwgk
Copy link
Collaborator

rwgk commented Oct 7, 2022

Oh, wait, I'll add a test (to be sure we don't miss this again).

@rwgk
Copy link
Collaborator

rwgk commented Oct 7, 2022

f626d7d adds a simple test.

Remark regarding implementation:

I tried to use reinterpet_cast but that did not work:

clang++ -o pybind11/tests/test_pytypes.os -c -std=c++17 -fPIC -fvisibility=hidden -O0 -g -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated -Wnon-virtual-dtor -Wunused-result -Werror -isystem /usr/include/python3.10 -isystem /usr/include/eigen3 -DPYBIND11_STRICT_ASSERTS_CLASS_HOLDER_VS_TYPE_CASTER_MIX -DPYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD_IF_AVAILABLE -DPYBIND11_TEST_BOOST -Ipybind11/include -I/usr/local/google/home/rwgk/forked/pybind11/include -I/usr/local/google/home/rwgk/clone/pybind11/include /usr/local/google/home/rwgk/forked/pybind11/tests/test_pytypes.cpp
/usr/local/google/home/rwgk/forked/pybind11/tests/test_pytypes.cpp:294:43: error: reinterpret_cast from 'std::nullptr_t' to 'void (*)(void *)' is not allowed
        return py::capsule((void *) 1234, reinterpret_cast<void (*)(void *)>(nullptr)); // #4221
                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

To be sure the test is actually exercising the fix:

I cherry-picked f626d7d on top of master, then ran test_pytypes:

Running tests in directory "/usr/local/google/home/rwgk/forked/pybind11/tests":
=========================================================== test session starts ============================================================
platform linux -- Python 3.10.7, pytest-7.1.2, pluggy-1.0.0
C++ Info: Debian Clang 14.0.6 C++17 __pybind11_internals_v4_clang_libstdcpp_cxxabi1002__
rootdir: /usr/local/google/home/rwgk/forked/pybind11/tests, configfile: pytest.ini
collected 79 items

test_pytypes.py ....................terminate called after throwing an instance of 'std::runtime_error'
  what():  Unable to get capsule context
Fatal Python error: Aborted

Current thread 0x00007fe523f4e740 (most recent call first):
  File "/usr/local/google/home/rwgk/forked/pybind11/tests/test_pytypes.py", line 304 in test_capsule
  ... (rest of trace omitted) ...

@rwgk
Copy link
Collaborator

rwgk commented Oct 7, 2022

@Skylion007 Does the added test look good to you?

tests/test_pytypes.cpp Outdated Show resolved Hide resolved
rwgk and others added 2 commits October 7, 2022 09:11
I tried this locally and it works!
I never knew that there are cases where `reinterpret_cast` does not work but `static_cast` does. Let's see if all compilers are happy with this.

Co-authored-by: Aaron Gokaslan <skylion.aaron@gmail.com>
@rwgk
Copy link
Collaborator

rwgk commented Oct 7, 2022

Thanks! Waiting a moment before merging this, until we have #4201 stable again.

@rwgk
Copy link
Collaborator

rwgk commented Oct 7, 2022

Thanks for the fix!

@rwgk rwgk merged commit 7c6f2f8 into pybind:master Oct 7, 2022
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label Oct 7, 2022
@Skylion007
Copy link
Collaborator

@galv @rwgk Out of curiosity, I noticed we store the destructor in the context, when PyCapsule's appear to have a dedicated slot for the destructor. Any idea why? @rwgk @galv @wjakob ?

@rwgk
Copy link
Collaborator

rwgk commented Oct 10, 2022

@galv @rwgk Out of curiosity, I noticed we store the destructor in the context, when PyCapsule's appear to have a dedicated slot for the destructor. Any idea why? @rwgk @galv @wjakob ?

Oh, same question, sorry I didn't see this before.

https://github.com/pybind/pybind11/pull/4232/files#r991640142

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants