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

[OTHER]: project using nanobind crashes because nanobind demangles strings returned by typeid() #946

Open
yurivict opened this issue Feb 21, 2025 · 7 comments

Comments

@yurivict
Copy link

yurivict commented Feb 21, 2025

Please don't use.

The python bindings of the proxsuite project crashes at the runtime with this stack:

>>> import proxsuite

Breakpoint 1.1, nanobind::detail::type_name (t=0x721ced10 <typeinfo for proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2>>)
    at /usr/local/lib/python3.11/site-packages/nanobind/src/nb_func.cpp:1448
1448        const char *name_in = t->name();
(gdb) bt
#0  nanobind::detail::type_name (t=0x721ced10 <typeinfo for proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2>>)
    at /usr/local/lib/python3.11/site-packages/nanobind/src/nb_func.cpp:1448
#1  0x0000000071d6a5df in nanobind::detail::nb_func_render_signature (f=0x7181e6a8, nb_signature_mode=false) at /usr/local/lib/python3.11/site-packages/nanobind/src/nb_func.cpp:1205
#2  0x0000000071d6a84a in nanobind::detail::nb_func_get_doc (self=0x7181e680) at /usr/local/lib/python3.11/site-packages/nanobind/src/nb_func.cpp:1364
#3  0x0000000071d6aad6 in nanobind::detail::nb_func_getattro (self=0x7181e680, name_=0x2c37c928 <_PyRuntime+25008>) at /usr/local/lib/python3.11/site-packages/nanobind/src/nb_func.cpp:1412
#4  0x000000002c0cef1a in _PyObject_LookupAttr () from /usr/local/lib/libpython3.11.so.1.0
#5  0x000000002c085d06 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#6  0x000000002c0ee25b in ?? () from /usr/local/lib/libpython3.11.so.1.0
#7  0x000000002c074e39 in _PyObject_MakeTpCall () from /usr/local/lib/libpython3.11.so.1.0
#8  0x00000000721c41d7 in nanobind::detail::obj_vectorcall (base=0x2c31e050 <PyProperty_Type>, args=0x7fffffff6dd8, nargsf=9223372036854775812, kwnames=0x0, method_call=false) at /usr/local/lib/python3.11/site-packages/nanobind/src/common.cpp:319
#9  0x00000000721c9545 in nanobind::detail::api<nanobind::handle>::operator()<(nanobind::rv_policy)1, nanobind::handle, nanobind::handle, nanobind::handle, nanobind::object&> (this=0x7fffffff6f38, args_=..., args_=..., args_=..., args_=...)
    at /usr/local/lib/python3.11/site-packages/nanobind/include/nanobind/nb_call.h:141
#10 0x00000000721c5359 in nanobind::detail::property_install_impl (tp=0x2c31e050 <PyProperty_Type>, scope=0x71a11f10, name=0x71da1efc "alphas", getter=0x7181e680, setter=0x0) at /usr/local/lib/python3.11/site-packages/nanobind/src/common.cpp:853
#11 0x00000000721c50b4 in nanobind::detail::property_install (scope=0x71a11f10, name=0x71da1efc "alphas", getter=0x7181e680, setter=0x0) at /usr/local/lib/python3.11/site-packages/nanobind/src/common.cpp:863
#12 0x0000000071f1534f in nanobind::class_<proxsuite::proxqp::dense::Workspace<double>>::def_prop_rw<nanobind::class_<proxsuite::proxqp::dense::Workspace<double>>::def_ro<proxsuite::proxqp::dense::Workspace<double>, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::Syst
emAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2>>(char const*, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable
)2> proxsuite::proxqp::dense::Workspace<double>::*)::{lambda(proxsuite::proxqp::dense::Workspace<double> const&)#1}&, decltype(nullptr)>(char const*, nanobind::class_<proxsuite::proxqp::dense::Workspace<double>>::def_ro<proxsuite::proxqp::dense::Workspace<double>, proxsuite::linalg::v
eg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2>>(char const*, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailab
le)2, (proxsuite::linalg::veg::mem::CopyAvailable)2> proxsuite::proxqp::dense::Workspace<double>::*)::{lambda(proxsuite::proxqp::dense::Workspace<double> const&)#1}&, decltype(nullptr)&&) (this=0x7fffffffaff8, name_=0x71da1efc "alphas", getter=..., 
    setter=<error reading variable: Attempt to dereference a generic pointer.>) at /usr/local/lib/python3.11/site-packages/nanobind/include/nanobind/nb_class.h:626
#13 nanobind::class_<proxsuite::proxqp::dense::Workspace<double>>::def_prop_ro<nanobind::class_<proxsuite::proxqp::dense::Workspace<double>>::def_ro<proxsuite::proxqp::dense::Workspace<double>, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::l
inalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2>>(char const*, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2> proxsuite::proxqp:
:dense::Workspace<double>::*)::{lambda(proxsuite::proxqp::dense::Workspace<double> const&)#1}>(char const*, nanobind::class_<proxsuite::proxqp::dense::Workspace<double>>::def_ro<proxsuite::proxqp::dense::Workspace<double>, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::me
m::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2>>(char const*, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAv
ailable)2> proxsuite::proxqp::dense::Workspace<double>::*)::{lambda(proxsuite::proxqp::dense::Workspace<double> const&)#1}&&) (this=0x7fffffffaff8, name_=0x71da1efc "alphas", getter=...) at /usr/local/lib/python3.11/site-packages/nanobind/include/nanobind/nb_class.h:652
#14 nanobind::class_<proxsuite::proxqp::dense::Workspace<double>>::def_ro<proxsuite::proxqp::dense::Workspace<double>, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailab
le)2>>(char const*, proxsuite::linalg::veg::Vec<double, proxsuite::linalg::veg::mem::SystemAlloc, (proxsuite::linalg::veg::mem::DtorAvailable)2, (proxsuite::linalg::veg::mem::CopyAvailable)2> proxsuite::proxqp::dense::Workspace<double>::*) (this=0x7fffffffaff8, 
    name=0x71da1efc "alphas", p=&proxsuite::proxqp::dense::Workspace<double>::alphas) at /usr/local/lib/python3.11/site-packages/nanobind/include/nanobind/nb_class.h:702
#15 proxsuite::proxqp::dense::python::exposeWorkspaceDense<double> (m=...) at /usr/ports/math/py-proxsuite/work-py311/proxsuite-0.7.1/bindings/python/src/expose-workspace.hpp:48
#16 0x0000000071eac07a in proxsuite::proxqp::python::exposeDenseAlgorithms<double> (m=...) at /usr/ports/math/py-proxsuite/work-py311/proxsuite-0.7.1/bindings/python/src/expose-all.cpp:47
#17 0x0000000071ea9c32 in proxsuite::proxqp::python::nanobind_init_proxsuite_pywrap (m=...) at /usr/ports/math/py-proxsuite/work-py311/proxsuite-0.7.1/bindings/python/src/expose-all.cpp:94
#18 0x0000000071ea9858 in PyInit_proxsuite_pywrap () at /usr/ports/math/py-proxsuite/work-py311/proxsuite-0.7.1/bindings/python/src/expose-all.cpp:76
#19 0x000000002c1aa566 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#20 0x000000002c0c9dc9 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#21 0x000000002c17451c in _PyEval_EvalFrameDefault () from /usr/local/lib/libpython3.11.so.1.0
#22 0x000000002c16379d in PyEval_EvalCode () from /usr/local/lib/libpython3.11.so.1.0
#23 0x000000002c15f206 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#24 0x000000002c0c9cae in ?? () from /usr/local/lib/libpython3.11.so.1.0
#25 0x000000002c17451c in _PyEval_EvalFrameDefault () from /usr/local/lib/libpython3.11.so.1.0
#26 0x000000002c176e45 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#27 0x000000002c077381 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#28 0x000000002c077160 in PyObject_CallMethodObjArgs () from /usr/local/lib/libpython3.11.so.1.0
#29 0x000000002c1a56f3 in PyImport_ImportModuleLevelObject () from /usr/local/lib/libpython3.11.so.1.0
#30 0x000000002c16e00d in _PyEval_EvalFrameDefault () from /usr/local/lib/libpython3.11.so.1.0
#31 0x000000002c16379d in PyEval_EvalCode () from /usr/local/lib/libpython3.11.so.1.0
#32 0x000000002c1ccebd in ?? () from /usr/local/lib/libpython3.11.so.1.0
#33 0x000000002c1cd756 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#34 0x000000002c1cc260 in _PyRun_InteractiveLoopObject () from /usr/local/lib/libpython3.11.so.1.0
#35 0x000000002c1cbaca in _PyRun_AnyFileObject () from /usr/local/lib/libpython3.11.so.1.0
#36 0x000000002c1f3d4e in ?? () from /usr/local/lib/libpython3.11.so.1.0
#37 0x000000002c1f307b in Py_RunMain () from /usr/local/lib/libpython3.11.so.1.0
#38 0x000000002c1f46c5 in ?? () from /usr/local/lib/libpython3.11.so.1.0
#39 0x000000002c1f4aea in Py_BytesMain () from /usr/local/lib/libpython3.11.so.1.0
#40 0x000000002c5980f4 in __libc_start1 (argc=1, argv=0x7fffffffe620, env=0x7fffffffe630, cleanup=<optimized out>, mainX=0x201770) at /disk-samsung/freebsd-src/lib/libc/csu/libc_start1.c:157
#41 0x00000000002016d0 in _start ()

The immediate cause of this crash is that abi::__cxa_demangle() returned nullptr on the string "N9proxsuite6linalg3veg3VecIdNS1_3mem11SystemAllocELNS3_13DtorAvailableE2ELNS3_13CopyAvailableE2EEE".

The name() member of std::type_info returns an implementation-defined string
signifying the type. The standard says you cannot count on any particular
interpretation of this string, but in practice it usually contains a mangled
version of the name.

In this particular case, you should add "_Z" to the front of the string, before
demangling.

nanobind-2.4.0
Python-3.11
clang-19
FreeBSD 14.2

@wjakob
Copy link
Owner

wjakob commented Feb 21, 2025

I have never heard of such behavior. Is it documented somewhere?

@wjakob
Copy link
Owner

wjakob commented Feb 22, 2025

Addendum: I suppose this is some FreeBSD-specific behavior?

@yurivict
Copy link
Author

Addendum: I suppose this is some FreeBSD-specific behavior?

No, this is clang behavior.
Mangling has nothing to do with FreeBSD.

@wjakob
Copy link
Owner

wjakob commented Feb 25, 2025

I can only repeat: I have never ever heard of such behavior. If you do have a link explaining that one has to manipulate the typeid string, then could you please post it? nanobind is used by a large number of projects and platforms, and this issue has never come up before, so something seems fishy here.

@oremanj
Copy link
Contributor

oremanj commented Feb 25, 2025

In this particular case, you should add "_Z" to the front of the string, before demangling.

The leading _Z is used in mangled names of symbols, because they need to start with a non-digit and not collide with valid C identifiers. (C reserves identifiers that start with underscore+capital.) typeinfo names are under no such constraints, so they don't get the leading _Z. On my system, c++filt -t (t for types) correctly processes the name you pasted with no modifications. https://itanium-cxx-abi.github.io/cxx-abi/abi.html#demangler says __cxa_demangle can accept type names without a leading _Z. So I think this is an issue with your system's C++ library.

I think it would be reasonable for nanobind to compromise on something like "check for nullptr and use the mangled name if it can't be demangled". I don't think adding the leading _Z is a good idea, nor will it work for built-in types and pointers; it only happens to work because the mangling for namespaced types and the mangling for namespaced variables use the same convention.

@markmi
Copy link

markmi commented Feb 26, 2025

17.7.3 Class type_info as in N4950: "The names, encoding rule, and collating sequence for types are all unspecified and may differ between programs." So any constraint making uniformity is more likely from ABI rules or such, rather than from the likes of the language standard.

Also: "const char* name() const noexcept Returns: An implementation-defined NTBS. Remarks: The message may be a null-terminated multibyte string (16.3.3.3.4.3), suitable for conversion and display as a string (23.4, 30.4.2.5)."

Also: cppreference notes that "Some implementations (such as MSVC, IBM, Oracle) produce a human-readable type name".

@markmi
Copy link

markmi commented Feb 26, 2025

At a related FreeBSD bugzilla entry, the following comment was added about another type of ABI handling of this subject area:

--- Comment . . . from David Chisnall <. . .> ---
There are some more recent experimental ABIs that return a truncated
cryptographic hash from name() and emit a map from the hash to the full type
name in the debug info section. This can have a big impact on code size (10+%
reduction), while still allowing demangling with a debugger.

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

No branches or pull requests

4 participants