From bda49efb14268f0746ba8098508f8dcd8ebc8bdf Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sat, 19 Dec 2020 16:11:39 -0800 Subject: [PATCH] Demonstration of Undefined Behavior in handling of shared_ptr holder. Based on https://godbolt.org/z/4fdjaW by jorgbrown@ (thanks Jorg!). --- tests/test_smart_ptr_private_first_base.cpp | 47 +++++++++++++++++++++ tests/test_smart_ptr_private_first_base.py | 9 ++++ 2 files changed, 56 insertions(+) create mode 100644 tests/test_smart_ptr_private_first_base.cpp create mode 100644 tests/test_smart_ptr_private_first_base.py diff --git a/tests/test_smart_ptr_private_first_base.cpp b/tests/test_smart_ptr_private_first_base.cpp new file mode 100644 index 000000000..862effb85 --- /dev/null +++ b/tests/test_smart_ptr_private_first_base.cpp @@ -0,0 +1,47 @@ +// Demonstration of Undefined Behavior in handling of shared_ptr holder, +// specifically: +// https://github.com/pybind/pybind11/blob/30eb39ed79d1e2eeff15219ac00773034300a5e6/include/pybind11/cast.h#L235 +// `return reinterpret_cast(vh[1]);` +// indirectly casts a `shared_ptr` reference to a `shared_ptr`. +// `test_smart_ptr_private_first_base.py` fails with an AssertionError and +// a subsequent Segmentation Fault (Linux, clang++ -std=c++17). + +#include + +#include "pybind11_tests.h" + +namespace pybind11_tests { +namespace smart_ptr_private_first_base { + +struct base { + base() : base_id(100) {} + virtual ~base() = default; + virtual int id() const { return base_id; } + int base_id; +}; + +struct private_first_base { // Any class with a virtual function will do. + virtual void some_other_virtual_function() const {} + virtual ~private_first_base() = default; +}; + +struct drvd : private private_first_base, public base { + int id() const override { return 2 * base_id; } +}; + +inline std::shared_ptr make_shared_drvd() { + return std::shared_ptr(new drvd); +} + +inline int pass_shared_base(std::shared_ptr b) { return b->id(); } + +TEST_SUBMODULE(smart_ptr_private_first_base, m) { + py::class_>(m, "base"); + py::class_>(m, "drvd"); + + m.def("make_shared_drvd", make_shared_drvd); + m.def("pass_shared_base", pass_shared_base); +} + +} // namespace smart_ptr_private_first_base +} // namespace pybind11_tests diff --git a/tests/test_smart_ptr_private_first_base.py b/tests/test_smart_ptr_private_first_base.py new file mode 100644 index 000000000..75ecfe6f1 --- /dev/null +++ b/tests/test_smart_ptr_private_first_base.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +import pytest + +from pybind11_tests import smart_ptr_private_first_base as m + +def test_make_pass(): + d = m.make_shared_drvd() + i = m.pass_shared_base(d) + assert i == 200