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

Heap buffer overflow in libdwarf on assertion failed in sanitizer build #123

Open
ThibaultLemaire opened this issue Feb 27, 2025 · 2 comments

Comments

@ThibaultLemaire
Copy link

Hi and thanks for this awesome lib!

I encountered a crash on my project when trying to use libassert with sanitizers enabled:

==512519==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6240000480f0 at pc 0x798d9a919163 bp 0x7ffe12fa5700 sp 0x7ffe12fa56f8
READ of size 8 at 0x6240000480f0 thread T0
    #0 0x798d9a919162 in dwarf_dealloc /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_alloc.c:925
    #1 0x798d9a83f166 in cpptrace::detail::libdwarf::die_object::get_string_attribute[abi:cxx11](unsigned short) const::{lambda(char*)#1}::operator()(char*) const /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf.hpp:152
    #2 0x798d9a867bb9 in cpptrace::detail::raii_wrapper<char*, cpptrace::detail::libdwarf::die_object::get_string_attribute(unsigned short) const::{lambda(char*)#1}, 0, 0>::~raii_wrapper() /home/user/project/build/_deps/cpptrace-src/src/utils/utils.hpp:231
    #3 0x798d9a83f7d4 in cpptrace::detail::libdwarf::die_object::get_string_attribute[abi:cxx11](unsigned short) const /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf.hpp:155
    #4 0x798d9a85022a in cpptrace::detail::libdwarf::dwarf_resolver::subprogram_symbol[abi:cxx11](cpptrace::detail::libdwarf::die_object const&, unsigned short) /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf_resolver.cpp:287
    #5 0x798d9a854bf0 in cpptrace::detail::libdwarf::dwarf_resolver::retrieve_symbol_for_subprogram[abi:cxx11](cpptrace::detail::libdwarf::die_object const&, cpptrace::detail::libdwarf::die_object const&, unsigned long long, unsigned short, std::vector<cpptrace::stacktrace_frame, std::allocator<cpptrace::stacktrace_frame> >&) /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf_resolver.cpp:426
    #6 0x798d9a858350 in cpptrace::detail::libdwarf::dwarf_resolver::retrieve_symbol(cpptrace::detail::libdwarf::die_object const&, unsigned long long, unsigned short, cpptrace::stacktrace_frame&, std::vector<cpptrace::stacktrace_frame, std::allocator<cpptrace::stacktrace_frame> >&) /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf_resolver.cpp:576
    #7 0x798d9a8641c5 in cpptrace::detail::libdwarf::dwarf_resolver::resolve_frame_core(cpptrace::object_frame const&, cpptrace::stacktrace_frame&, std::vector<cpptrace::stacktrace_frame, std::allocator<cpptrace::stacktrace_frame> >&) (/home/user/project/build/lib/libcpptrace.so.0+0x8641c5)
    #8 0x798d9a864fdb in cpptrace::detail::libdwarf::dwarf_resolver::resolve_frame(cpptrace::object_frame const&) /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf_resolver.cpp:1003
    #9 0x798d9a900d53 in cpptrace::detail::libdwarf::resolve_frames(std::vector<cpptrace::object_frame, std::allocator<cpptrace::object_frame> > const&) /home/user/project/build/_deps/cpptrace-src/src/symbols/symbols_with_libdwarf.cpp:108
    #10 0x798d9a8e8fb5 in cpptrace::detail::resolve_frames(std::vector<unsigned long, std::allocator<unsigned long> > const&) /home/user/project/build/_deps/cpptrace-src/src/symbols/symbols_core.cpp:138
    #11 0x798d9a7e3cbb in cpptrace::raw_trace::resolve() const /home/user/project/build/_deps/cpptrace-src/src/cpptrace.cpp:63
    #12 0x798db6a547d6 in libassert::assertion_info::get_stacktrace() const /home/user/project/build/_deps/libassert-src/src/assert.cpp:593
    #13 0x798db6a53437 in libassert::assertion_info::get_path_handler() const /home/user/project/build/_deps/libassert-src/src/assert.cpp:559
    #14 0x798db6a561ca in libassert::assertion_info::tagline[abi:cxx11](libassert::color_scheme const&) const /home/user/project/build/_deps/libassert-src/src/assert.cpp:620
    #15 0x798db6a581fc in libassert::assertion_info::to_string[abi:cxx11](int, libassert::color_scheme const&) const /home/user/project/build/_deps/libassert-src/src/assert.cpp:672
    #16 0x798db6a4bc60 in libassert::default_failure_handler(libassert::assertion_info const&) /home/user/project/build/_deps/libassert-src/src/assert.cpp:440
    #17 0x798db6a4c044 in libassert::detail::fail(libassert::assertion_info const&) /home/user/project/build/_deps/libassert-src/src/assert.cpp:477
    #18 0x1571b0f in void libassert::detail::process_assert_fail<int const&, int const&, libassert::detail::ops::eq, libassert::detail::pretty_function_name_wrapper>(libassert::detail::expression_decomposer<int const&, int const&, libassert::detail::ops::eq>&, libassert::detail::assert_static_parameters const*, libassert::detail::pretty_function_name_wrapper&&) /home/user/project/build/_deps/libassert-src/include/libassert/assert.hpp:455
    #19 0x156d741 in void libassert::detail::process_assert_fail_n<int const&, int const&, libassert::detail::ops::eq, libassert::detail::pretty_function_name_wrapper>(libassert::detail::expression_decomposer<int const&, int const&, libassert::detail::ops::eq>, libassert::detail::assert_static_parameters const*, libassert::detail::pretty_function_name_wrapper&&) /home/user/project/build/_deps/libassert-src/include/libassert/assert.hpp:499
    <...> SOME CALLS LEFT OUT
    #26 0xc55fe1 in main /home/user/project/tester/tester.cc:192
    #27 0x798d9923314d in __libc_start_call_main (/nix/store/3dyw8dzj9ab4m8hv5dpyx7zii8d0w6fi-glibc-2.39-52/lib/libc.so.6+0x2a14d) (BuildId: 2f905760df53ce9c82feec29b6013b6a72c58c95)
    #28 0x798d99233208 in __libc_start_main_alias_1 (/nix/store/3dyw8dzj9ab4m8hv5dpyx7zii8d0w6fi-glibc-2.39-52/lib/libc.so.6+0x2a208) (BuildId: 2f905760df53ce9c82feec29b6013b6a72c58c95)
    #29 0x75e1d4 in _start (/home/user/project/build/bin/mybin+0x75e1d4)

0x6240000480f0 is located 16 bytes before 7122-byte region [0x624000048100,0x624000049cd2)
allocated by thread T0 here:
    #0 0x798dcbadc04f in __interceptor_malloc (/nix/store/wd68m1hnxwz2b3kazip9m9kmjg0my6gd-gcc-13.3.0-lib/lib/libasan.so.8+0xdc04f)
    #1 0x798d9aad3423 in elf_load_nolibelf_section /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_elfread.c:230
    #2 0x798d9a9c3503 in _dwarf_load_section /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_init_finish.c:1442
    #3 0x798d9a96c9ce in _dwarf_extract_local_debug_str_string_given_offset /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_form.c:1978
    #4 0x798d9a96e6fb in dwarf_formstring /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_form.c:2167
    #5 0x798d9aa75b29 in dwarf_die_text /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_query.c:854
    #6 0x798d9a928a8b in set_producer_type /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_die_deliv.c:1283
    #7 0x798d9a929694 in find_cu_die_base_fields /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_die_deliv.c:1375
    #8 0x798d9a924ed8 in finish_cu_context_via_cudie_inner /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_die_deliv.c:696
    #9 0x798d9a92c21d in finish_up_cu_context_from_cudie /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_die_deliv.c:1734
    #10 0x798d9a92d2aa in _dwarf_create_a_new_cu_context_record_on_list /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_die_deliv.c:1878
    #11 0x798d9a938886 in dwarf_offdie_b /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_die_deliv.c:3296
    #12 0x798d9a87f2b2 in int cpptrace::detail::libdwarf::dwarf_resolver::wrap<Dwarf_Debug_s*, unsigned long long, int, Dwarf_Die_s**, Dwarf_Error_s**, cpptrace::detail::raii_wrapper<Dwarf_Debug_s*, void (*)(Dwarf_Debug_s*), 0, 0>&, unsigned long long&, bool, Dwarf_Die_s**, 0>(int (*)(Dwarf_Debug_s*, unsigned long long, int, Dwarf_Die_s**, Dwarf_Error_s**), cpptrace::detail::raii_wrapper<Dwarf_Debug_s*, void (*)(Dwarf_Debug_s*), 0, 0>&, unsigned long long&, bool&&, Dwarf_Die_s**&&) const /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf_resolver.cpp:97
    #13 0x798d9a85fe7c in cpptrace::detail::libdwarf::dwarf_resolver::lookup_cu(unsigned long long) /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf_resolver.cpp:802
    #14 0x798d9a863b5c in cpptrace::detail::libdwarf::dwarf_resolver::resolve_frame_core(cpptrace::object_frame const&, cpptrace::stacktrace_frame&, std::vector<cpptrace::stacktrace_frame, std::allocator<cpptrace::stacktrace_frame> >&) (/home/user/project/build/lib/libcpptrace.so.0+0x863b5c)
    #15 0x798d9a864fdb in cpptrace::detail::libdwarf::dwarf_resolver::resolve_frame(cpptrace::object_frame const&) /home/user/project/build/_deps/cpptrace-src/src/symbols/dwarf/dwarf_resolver.cpp:1003
    #16 0x798d9a900d53 in cpptrace::detail::libdwarf::resolve_frames(std::vector<cpptrace::object_frame, std::allocator<cpptrace::object_frame> > const&) /home/user/project/build/_deps/cpptrace-src/src/symbols/symbols_with_libdwarf.cpp:108
    #17 0x798d9a8e8fb5 in cpptrace::detail::resolve_frames(std::vector<unsigned long, std::allocator<unsigned long> > const&) /home/user/project/build/_deps/cpptrace-src/src/symbols/symbols_core.cpp:138
    #18 0x798d9a7e3cbb in cpptrace::raw_trace::resolve() const /home/user/project/build/_deps/cpptrace-src/src/cpptrace.cpp:63
    #19 0x798db6a547d6 in libassert::assertion_info::get_stacktrace() const /home/user/project/build/_deps/libassert-src/src/assert.cpp:593
    #20 0x798db6a53437 in libassert::assertion_info::get_path_handler() const /home/user/project/build/_deps/libassert-src/src/assert.cpp:559
    #21 0x798db6a561ca in libassert::assertion_info::tagline[abi:cxx11](libassert::color_scheme const&) const /home/user/project/build/_deps/libassert-src/src/assert.cpp:620
    #22 0x798db6a581fc in libassert::assertion_info::to_string[abi:cxx11](int, libassert::color_scheme const&) const /home/user/project/build/_deps/libassert-src/src/assert.cpp:672
    #23 0x798db6a4bc60 in libassert::default_failure_handler(libassert::assertion_info const&) /home/user/project/build/_deps/libassert-src/src/assert.cpp:440
    #24 0x798db6a4c044 in libassert::detail::fail(libassert::assertion_info const&) /home/user/project/build/_deps/libassert-src/src/assert.cpp:477
    #25 0x1571b0f in void libassert::detail::process_assert_fail<int const&, int const&, libassert::detail::ops::eq, libassert::detail::pretty_function_name_wrapper>(libassert::detail::expression_decomposer<int const&, int const&, libassert::detail::ops::eq>&, libassert::detail::assert_static_parameters const*, libassert::detail::pretty_function_name_wrapper&&) /home/user/project/build/_deps/libassert-src/include/libassert/assert.hpp:455
    #26 0x156d741 in void libassert::detail::process_assert_fail_n<int const&, int const&, libassert::detail::ops::eq, libassert::detail::pretty_function_name_wrapper>(libassert::detail::expression_decomposer<int const&, int const&, libassert::detail::ops::eq>, libassert::detail::assert_static_parameters const*, libassert::detail::pretty_function_name_wrapper&&) /home/user/project/build/_deps/libassert-src/include/libassert/assert.hpp:499
    <...> SOME CALLS LEFT OUT

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/user/project/build/_deps/libdwarf-src/src/lib/libdwarf/dwarf_alloc.c:925 in dwarf_dealloc
Shadow bytes around the buggy address:
  0x624000047e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x624000047e80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x624000047f00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x624000047f80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x624000048000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x624000048080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]fa
  0x624000048100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x624000048180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x624000048200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x624000048280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x624000048300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==512519==ABORTING

It looks like it was caused by my (mis)usage of add_compile_options and add_link_options in my CmakeLists.txt to add -fsanitize= which probably leaked the flags to libdwarf.

I was not able to reproduce with a smaller project. All I got were link errors about various symbols from ASan not being found by libdwarf. This led me to solve the issue by replacing add_{compile,link}_options with target_{compile,link}_options.

- add_compile_options(-fsanitize=address)
- add_link_options(-fsanitize=address)
+ target_compile_options(my_target PRIVATE -fsanitize=address)
+ target_link_options(my_target PRIVATE -fsanitize=address)

It's not really an issue of libaddress, but is/was a blocker to using it, so I'm putting it here in case it helps someone else 🤷

@jeremy-rifkin
Copy link
Owner

Hi, thanks for the report. I’ll look into this later today.

@jeremy-rifkin
Copy link
Owner

Leaking the sanitizer flags to libdwarf is ok, it is often helpful to compile an entire project (including libraries) with sanitizers. I'm glad you're able to work around this so you could unblock.

In general any sanitizer issue is a bug (false positives are pretty rare) so I am definitely interested in tracking this issue down.

Looking into this, the error is really odd. cpptrace::detail::libdwarf::die_object::get_string_attribute is a heavily used codepath and I've never seen any problems before, cpptrace should be freeing things properly. The traces make it looks like dwarf_dealloc is trying to access data in the loaded debug information which doesn't make much sense to me. If the string isn't allocated it shouldn't be deallocated but it appears it is definitely correct to dwarf_dealloc the string from dwarf_formstring, so I'm a bit confused. I've opened an issue upstream to ask about this.

It would be really helpful to get some additional information here about what compiler you're using and how you're compiling. Ideally I'd like to be able to reproduce this but that can sometimes be hard. If you are able to send the compiled program where this was failing that could be helpful for diagnosing what's going on with libdwarf and cpptrace here.

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

2 participants