Skip to content

librbd: use try_lexical_convert instead of throwing lexical_cast#67585

Open
tchaikov wants to merge 1 commit intoceph:mainfrom
tchaikov:librbd-silence-asan
Open

librbd: use try_lexical_convert instead of throwing lexical_cast#67585
tchaikov wants to merge 1 commit intoceph:mainfrom
tchaikov:librbd-silence-asan

Conversation

@tchaikov
Copy link
Contributor

@tchaikov tchaikov commented Mar 1, 2026

rbd_features_from_string() uses boost::lexical_cast which throws bad_lexical_cast when the input is not numeric. The exception is caught and handled as "parse as feature name list instead".

This is normal control flow, but when ASAN's __cxa_throw interceptor is misconfigured (e.g. with intercept_cxx_exceptions=0 leaving real___cxa_throw NULL), any exception causes a CHECK failure. Even with a correctly configured ASAN, throwing exceptions during config initialization adds overhead.

Replace the try/catch pattern with boost::conversion::try_lexical_convert, which returns false on parse failure instead of throwing. This eliminates the exception entirely, making the code more efficient and avoiding any interaction with ASAN's exception interceptor.

The ASan report:

AddressSanitizer: CHECK failed: asan_interceptors.cpp:320 "((__interception::real___cxa_throw)) != (0)" (0x0, 0x0) (tid=30529)
    #0 0x7d668fac9a09 in CheckUnwind ../../../../src/libsanitizer/asan/asan_rtl.cpp:67
    #1 0x7d668faec105 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) ../../../../src/libsanitizer/sanitizer_common/sanitizer_termination.cpp:86
    #2 0x7d668fa4b194 in __interceptor___cxa_throw ../../../../src/libsanitizer/asan/asan_interceptors.cpp:320
    #3 0x7d668ae4ec0f in void boost::throw_exception<boost::bad_lexical_cast>(boost::bad_lexical_cast const&) /opt/ceph/include/boost/throw_exception.hpp:165
    #4 0x7d668c1e1e0b in void boost::conversion::detail::throw_bad_cast<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long>() /opt/ceph/include/boost/lexical_cast/bad_lexical_cast.hpp:93
    #5 0x7d668c1e0e05 in unsigned long boost::lexical_cast<unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /opt/ceph/include/boost/lexical_cast.hpp:43
    #6 0x7d668c1df609 in librbd::rbd_features_from_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::ostream*) /ceph/src/librbd/Features.cc:67
    #7 0x7d668b255a35 in get_rbd_options()::$_2::operator()(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const src/common/options/rbd_options.cc:44
    #8 0x7d668b255806 in int std::__invoke_impl<int, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*>(std::__invoke_other, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&) /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:61
    #9 0x7d668b255754 in std::enable_if<is_invocable_r_v<int, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*>, int>::type std::__invoke_r<int, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*>(get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&) /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:114
    #10 0x7d668b25563c in std::_Function_handler<int (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*), get_rbd_options()::$_2>::_M_invoke(std::_Any_data const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&) /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:290
    #11 0x7d668af924f1 in std::function<int (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)>::operator()(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:591
    #12 0x7d668af8c495 in Option::pre_validate(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const /ceph/src/common/options.cc:94
    #13 0x7d668aef9ec3 in md_config_t::md_config_t(ConfigValues&, ConfigTracker const&, bool) /ceph/src/common/config.cc:208
    #14 0x7d668ae0040d in ceph::common::ConfigProxy::ConfigProxy(bool) /ceph/src/common/config_proxy.h:70
    #15 0x7d668adf3f5d in ceph::common::CephContext::CephContext(unsigned int, ceph::common::CephContext::create_options const&) /ceph/src/common/ceph_context.cc:730
    #16 0x7d668adf3785 in ceph::common::CephContext::CephContext(unsigned int, code_environment_t, int) /ceph/src/common/ceph_context.cc:724
    #17 0x7d668aef5ee6 in common_preinit(CephInitParameters const&, code_environment_t, int) /ceph/src/common/common_init.cc:40
    #18 0x7d668d4a47af in rados_create_cct(char const*, CephInitParameters*) /ceph/src/librados/librados_c.cc:120
    #19 0x7d668d4a49db in _rados_create2 /ceph/src/librados/librados_c.cc:168
    #20 0x7d668d94dc4b in __pyx_pf_5rados_5Rados_2__setup /ceph/build/src/pybind/rados/rados_processed.c:13219
    #21 0x7d668d94dc4b in __pyx_pw_5rados_5Rados_3__setup /ceph/build/src/pybind/rados/rados_processed.c:12703
    #22 0x7d668d94a347 in __Pyx_CyFunction_CallAsMethod /ceph/build/src/pybind/rados/rados_processed.c:93157
    #23 0x58ac5086d0ba in _PyObject_MakeTpCall (/usr/bin/python3.10+0x1810ba)
    #24 0x58ac508843da  (/usr/bin/python3.10+0x1983da)
    #25 0x58ac50885076 in PyVectorcall_Call (/usr/bin/python3.10+0x199076)
    #26 0x7d668d94cdd7 in __Pyx_PyObject_Call /ceph/build/src/pybind/rados/rados_processed.c:90994
    #27 0x7d668d94cdd7 in __pyx_pf_5rados_5Rados___init__ /ceph/build/src/pybind/rados/rados_processed.c:12474
    #28 0x7d668d94cdd7 in __pyx_pw_5rados_5Rados_1__init__ /ceph/build/src/pybind/rados/rados_processed.c:12443
    #29 0x58ac5086d43a  (/usr/bin/python3.10+0x18143a)
    #30 0x58ac50884d3a in PyObject_Call (/usr/bin/python3.10+0x198d3a)
    #31 0x58ac508637de in _PyEval_EvalFrameDefault (/usr/bin/python3.10+0x1777de)
    #32 0x58ac5087702b in _PyFunction_Vectorcall (/usr/bin/python3.10+0x18b02b)
    #33 0x58ac508615fe in _PyEval_EvalFrameDefault (/usr/bin/python3.10+0x1755fe)
    #34 0x58ac5087702b in _PyFunction_Vectorcall (/usr/bin/python3.10+0x18b02b)
    #35 0x58ac508615fe in _PyEval_EvalFrameDefault (/usr/bin/python3.10+0x1755fe)

Contribution Guidelines

  • To sign and title your commits, please refer to Submitting Patches to Ceph.

  • If you are submitting a fix for a stable branch (e.g. "quincy"), please refer to Submitting Patches to Ceph - Backports for the proper workflow.

  • When filling out the below checklist, you may click boxes directly in the GitHub web UI. When entering or editing the entire PR message in the GitHub web UI editor, you may also select a checklist item by adding an x between the brackets: [x]. Spaces and capitalization matter when checking off items this way.

Checklist

  • Tracker (select at least one)
    • References tracker ticket
    • Very recent bug; references commit where it was introduced
    • New feature (ticket optional)
    • Doc update (no ticket needed)
    • Code cleanup (no ticket needed)
  • Component impact
    • Affects Dashboard, opened tracker ticket
    • Affects Orchestrator, opened tracker ticket
    • No impact that needs to be tracked
  • Documentation (select at least one)
    • Updates relevant documentation
    • No doc update is appropriate
  • Tests (select at least one)
Show available Jenkins commands

You must only issue one Jenkins command per-comment. Jenkins does not understand
comments with more than one command.

@github-actions github-actions bot added the rbd label Mar 1, 2026
rbd_features_from_string() uses boost::lexical_cast which throws
bad_lexical_cast when the input is not numeric. The exception is caught
and handled as "parse as feature name list instead".

This is normal control flow, but when ASAN's __cxa_throw interceptor is
misconfigured (e.g. with intercept_cxx_exceptions=0 leaving real___cxa_throw
NULL), any exception causes a CHECK failure. Even with a correctly configured
ASAN, throwing exceptions during config initialization adds overhead.

Replace the try/catch pattern with boost::conversion::try_lexical_convert,
which returns false on parse failure instead of throwing. This eliminates
the exception entirely, making the code more efficient and avoiding any
interaction with ASAN's exception interceptor.

The ASan report:

```
AddressSanitizer: CHECK failed: asan_interceptors.cpp:320 "((__interception::real___cxa_throw)) != (0)" (0x0, 0x0) (tid=30529)
    #0 0x7d668fac9a09 in CheckUnwind ../../../../src/libsanitizer/asan/asan_rtl.cpp:67
    #1 0x7d668faec105 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long) ../../../../src/libsanitizer/sanitizer_common/sanitizer_termination.cpp:86
    #2 0x7d668fa4b194 in __interceptor___cxa_throw ../../../../src/libsanitizer/asan/asan_interceptors.cpp:320
    #3 0x7d668ae4ec0f in void boost::throw_exception<boost::bad_lexical_cast>(boost::bad_lexical_cast const&) /opt/ceph/include/boost/throw_exception.hpp:165
    #4 0x7d668c1e1e0b in void boost::conversion::detail::throw_bad_cast<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long>() /opt/ceph/include/boost/lexical_cast/bad_lexical_cast.hpp:93
    #5 0x7d668c1e0e05 in unsigned long boost::lexical_cast<unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /opt/ceph/include/boost/lexical_cast.hpp:43
    #6 0x7d668c1df609 in librbd::rbd_features_from_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::ostream*) /ceph/src/librbd/Features.cc:67
    #7 0x7d668b255a35 in get_rbd_options()::$_2::operator()(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const src/common/options/rbd_options.cc:44
    #8 0x7d668b255806 in int std::__invoke_impl<int, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*>(std::__invoke_other, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&) /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:61
    #9 0x7d668b255754 in std::enable_if<is_invocable_r_v<int, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*>, int>::type std::__invoke_r<int, get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*>(get_rbd_options()::$_2&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&) /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/invoke.h:114
    #10 0x7d668b25563c in std::_Function_handler<int (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*), get_rbd_options()::$_2>::_M_invoke(std::_Any_data const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*&&) /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:290
    #11 0x7d668af924f1 in std::function<int (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)>::operator()(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/std_function.h:591
    #12 0x7d668af8c495 in Option::pre_validate(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*) const /ceph/src/common/options.cc:94
    ceph#13 0x7d668aef9ec3 in md_config_t::md_config_t(ConfigValues&, ConfigTracker const&, bool) /ceph/src/common/config.cc:208
    ceph#14 0x7d668ae0040d in ceph::common::ConfigProxy::ConfigProxy(bool) /ceph/src/common/config_proxy.h:70
    ceph#15 0x7d668adf3f5d in ceph::common::CephContext::CephContext(unsigned int, ceph::common::CephContext::create_options const&) /ceph/src/common/ceph_context.cc:730
    ceph#16 0x7d668adf3785 in ceph::common::CephContext::CephContext(unsigned int, code_environment_t, int) /ceph/src/common/ceph_context.cc:724
    ceph#17 0x7d668aef5ee6 in common_preinit(CephInitParameters const&, code_environment_t, int) /ceph/src/common/common_init.cc:40
    ceph#18 0x7d668d4a47af in rados_create_cct(char const*, CephInitParameters*) /ceph/src/librados/librados_c.cc:120
    ceph#19 0x7d668d4a49db in _rados_create2 /ceph/src/librados/librados_c.cc:168
    ceph#20 0x7d668d94dc4b in __pyx_pf_5rados_5Rados_2__setup /ceph/build/src/pybind/rados/rados_processed.c:13219
    ceph#21 0x7d668d94dc4b in __pyx_pw_5rados_5Rados_3__setup /ceph/build/src/pybind/rados/rados_processed.c:12703
    ceph#22 0x7d668d94a347 in __Pyx_CyFunction_CallAsMethod /ceph/build/src/pybind/rados/rados_processed.c:93157
    ceph#23 0x58ac5086d0ba in _PyObject_MakeTpCall (/usr/bin/python3.10+0x1810ba)
    ceph#24 0x58ac508843da  (/usr/bin/python3.10+0x1983da)
    ceph#25 0x58ac50885076 in PyVectorcall_Call (/usr/bin/python3.10+0x199076)
    ceph#26 0x7d668d94cdd7 in __Pyx_PyObject_Call /ceph/build/src/pybind/rados/rados_processed.c:90994
    ceph#27 0x7d668d94cdd7 in __pyx_pf_5rados_5Rados___init__ /ceph/build/src/pybind/rados/rados_processed.c:12474
    ceph#28 0x7d668d94cdd7 in __pyx_pw_5rados_5Rados_1__init__ /ceph/build/src/pybind/rados/rados_processed.c:12443
    ceph#29 0x58ac5086d43a  (/usr/bin/python3.10+0x18143a)
    ceph#30 0x58ac50884d3a in PyObject_Call (/usr/bin/python3.10+0x198d3a)
    ceph#31 0x58ac508637de in _PyEval_EvalFrameDefault (/usr/bin/python3.10+0x1777de)
    ceph#32 0x58ac5087702b in _PyFunction_Vectorcall (/usr/bin/python3.10+0x18b02b)
    ceph#33 0x58ac508615fe in _PyEval_EvalFrameDefault (/usr/bin/python3.10+0x1755fe)
    ceph#34 0x58ac5087702b in _PyFunction_Vectorcall (/usr/bin/python3.10+0x18b02b)
    ceph#35 0x58ac508615fe in _PyEval_EvalFrameDefault (/usr/bin/python3.10+0x1755fe)
```

Signed-off-by: Kefu Chai <k.chai@proxmox.com>
@tchaikov tchaikov force-pushed the librbd-silence-asan branch from 54c750f to 4a09bb2 Compare March 1, 2026 05:11
@tchaikov tchaikov mentioned this pull request Mar 1, 2026
14 tasks
@tchaikov
Copy link
Contributor Author

tchaikov commented Mar 2, 2026

an alternative of this change was created at #66875. but i think this one is a better fix. as we should use exception for exception, not as a part of the regular control flow.

@tchaikov tchaikov requested a review from a team March 4, 2026 03:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant