-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
[BUG]: Wrong caching of the overrides #3369
Comments
Hi @Trigve, I looked a bit although I don't have much relevant background. I don't use embedding and I'm not familiar with the overload caching functionality. But this sounds good to me:
If you want to work on that, please send the PR to me for review. It doesn't have to be polished or even "finished", if you reach certain decision points just push what you have and we'll take a look. Thinking a bit more high level, could you work around the caching bug by importing Thinking even more high level, I usually strongly advise against embedding. You can only safely start and stop the interpreter once in a process anyway (see Bugs and caveats here). If you can organize your system to let Python be the main, and your main be an extension, you'll be more on a beaten path and probably encounter less bugs. |
Thanks for the reply @rwgk
I have a git patches though but if you need the PR I'll try it.
I'm importing the
In my case that is not an option (Our C++ framework does us python as scripting language). I only start/finalize the python interpreter once (are using process isolation, that is process is spawn when needed) so that is also not a problem. |
Yes. We can assist, but not drive in many cases, like this one. (We're maintaining pybind11 in volunteer time.)
Could you turn that into a non-embedding unit test? I have easy access to clang sanitizers (asan, msan), but only for extensions (not embedding). |
I've made a PR #3462
The tests are included in the PR (embedded and non-embedded also). Be aware it is not DIRECT use after free (I've used apostrophes to distinguish it), because the freed memory isn't read/written, just use as the key in the cache. |
Required prerequisites
Problem description
pybind11 2.6.2 (or smart holder branch), Python 3.7.10, VS 2019 C++20 (compiled as x86), Windows 10 x64
There is problem of caching the python functions overrides. The problem could be simulated using the code below.
In the example, there are 2 function in python.
func()
does returns the python-derived class in which the method is overriden from the base class.func2()
does returns python-derived class BUT NO method is overriden, therefor C++ function is called in the end.The problem is, that the cache (
get_internals().inactive_override_cache
) does store the pair<handle of the type, func name address>. But if the type is deallocated, it isn't removed from the cache and in future there could be match of the given type handle and the function name address. The problem seems to be withpybind11_meta_dealloc()
. Because the type isn't the one registered with pybind11, it doesn't erase it from the override cache. (edit: Looking to it more carefully, the actual problem could be inall_type_info_get_cache()
. Because, there type is removed from theregistered_types_py
, but the override cache isn't checked).On the line marked "// (2)" the
p_obj2->func();
call should be store/retrieved from the override cache as it always resolve to the C++ function.The code on line marked "// (1)" should ALWAYS returns
42
but on some random iteration it would return 0 (and assertion is hit "// (3)"), because the cache would hold the same handle and function name address and that means that function is in C++ and not in python instance (which is wrong).This does occurs using #1546 (comment) and also using smart holder branch https://github.com/pybind/pybind11/tree/smart_holder (@rwgk).
The poor-man's fix is to replace inactive_override_cache
std::unordered_set<std::pair<const PyObject *, const char *>, override_hash>
withstd::unordered_map<const PyObject *, std::unordered_set<const char *>>
and inpybind11_meta_dealloc()
erase also the types not registered by pybind11. It does work in my production code (edit: As noted above, the better fix could be inall_type_info_get_cache()
and erase the override from the cache when the type is unregistered).Workaround for python code: Move the
class Test(core.DialogLogic):
to the "global" scope. edit: This is only partial workaround. If wholepybind11::exec()
is moved into the loop, assertion happens too.Reproducible example code
The text was updated successfully, but these errors were encountered: