Skip to content

Commit e0207d6

Browse files
committed
Demonstration of Undefined Behavior in handling of shared_ptr holder.
Based on https://godbolt.org/z/4fdjaW by jorgbrown@ (thanks Jorg!).
1 parent 3b34392 commit e0207d6

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Demonstration of Undefined Behavior in handling of shared_ptr holder,
2+
// specifically:
3+
// https://github.com/pybind/pybind11/blob/30eb39ed79d1e2eeff15219ac00773034300a5e6/include/pybind11/cast.h#L235
4+
// `return reinterpret_cast<H &>(vh[1]);`
5+
// indirectly casts a `shared_ptr<drvd>` reference to a `shared_ptr<base>`.
6+
// `test_smart_ptr_private_first_base.py` fails with an AssertionError and
7+
// a subsequent Segmentation Fault (Linux, clang++ -std=c++17).
8+
9+
#include <memory>
10+
11+
#include "pybind11_tests.h"
12+
13+
namespace pybind11_tests {
14+
namespace smart_ptr_private_first_base {
15+
16+
struct base {
17+
base() : base_id(100) {}
18+
virtual ~base() = default;
19+
virtual int id() const { return base_id; }
20+
int base_id;
21+
};
22+
23+
struct private_first_base { // Any class with a virtual function will do.
24+
virtual void some_other_virtual_function() const {}
25+
virtual ~private_first_base() = default;
26+
};
27+
28+
struct drvd : private private_first_base, public base {
29+
int id() const override { return 2 * base_id; }
30+
};
31+
32+
inline std::shared_ptr<drvd> make_shared_drvd() {
33+
return std::shared_ptr<drvd>(new drvd);
34+
}
35+
36+
inline int pass_shared_base(std::shared_ptr<base> b) { return b->id(); }
37+
38+
TEST_SUBMODULE(smart_ptr_private_first_base, m) {
39+
py::class_<base, std::shared_ptr<base>>(m, "base");
40+
py::class_<drvd, base, std::shared_ptr<drvd>>(m, "drvd");
41+
42+
m.def("make_shared_drvd", make_shared_drvd);
43+
m.def("pass_shared_base", pass_shared_base);
44+
}
45+
46+
} // namespace smart_ptr_private_first_base
47+
} // namespace pybind11_tests
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
4+
from pybind11_tests import smart_ptr_private_first_base as m
5+
6+
def test_make_pass():
7+
d = m.make_shared_drvd()
8+
i = m.pass_shared_base(d)
9+
assert i == 200

0 commit comments

Comments
 (0)