Skip to content

Commit

Permalink
Porting additional demonstration of pybind11 Undefined Behavior in ha…
Browse files Browse the repository at this point in the history
…ndling of shared_ptr holder.

test_make_drvd_up_cast_pass_drvd fails with pybind11 (segfault) but passes with Boost.Python.

The root cause is the explicit cast in the pybind11 code here:
https://github.com/pybind/pybind11/blob/30eb39ed79d1e2eeff15219ac00773034300a5e6/include/pybind11/pybind11.h#L1505
    init_holder(inst, v_h, (const holder_type *) holder_ptr, v_h.value_ptr<type>());
  • Loading branch information
Ralf W. Grosse-Kunstleve committed Dec 22, 2020
1 parent debf3a5 commit 7d7494a
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 3 deletions.
7 changes: 7 additions & 0 deletions smart_ptr_private_first_base_ext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ inline std::shared_ptr<drvd> make_shared_drvd() {
return std::shared_ptr<drvd>(new drvd);
}

inline std::shared_ptr<base> make_shared_drvd_up_cast() {
return std::shared_ptr<base>(new drvd);
}

inline int pass_shared_base(std::shared_ptr<base> b) { return b->id(); }
inline int pass_shared_drvd(std::shared_ptr<drvd> d) { return d->id(); }

} // namespace

Expand All @@ -42,5 +47,7 @@ BOOST_PYTHON_MODULE(rwgk_tbx_smart_ptr_private_first_base_ext)
py::class_<drvd, py::bases<base>, std::shared_ptr<drvd>>("drvd");

py::def("make_shared_drvd", make_shared_drvd);
py::def("make_shared_drvd_up_cast", make_shared_drvd_up_cast);
py::def("pass_shared_base", pass_shared_base);
py::def("pass_shared_drvd", pass_shared_drvd);
}
21 changes: 18 additions & 3 deletions tst_smart_ptr_private_first_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,27 @@
import rwgk_tbx.smart_ptr_private_first_base as m
import sys


def test_make_drvd_pass_base():
d = m.make_shared_drvd()
i = m.pass_shared_base(d)
assert i == 200


def test_make_drvd_up_cast_pass_drvd():
b = m.make_shared_drvd_up_cast()
# the base return is up-cast immediately.
assert b.__class__.__name__ == "drvd"
i = m.pass_shared_drvd(b)
assert i == 200


def run(args):
assert not args
d = m.make_shared_drvd()
i = m.pass_shared_base(d)
assert i == 200
test_make_drvd_pass_base()
test_make_drvd_up_cast_pass_drvd()
print("Done.")


if (__name__ == "__main__"):
run(sys.argv[1:])

0 comments on commit 7d7494a

Please sign in to comment.