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

Allow the Catch2 library to be compiled with address sanitizer enabled #2866

Open
joesdiner opened this issue May 1, 2024 · 0 comments
Open

Comments

@joesdiner
Copy link

Some of this is wandering into areas I don't have a deep knowledge of, so I appreciate any corrections/comments

Description

Add a cmake configuration option to easily compile the Catch2 library with address sanitizer enabled to prevent address sanitizer from reporting certain false positives.

Additional context

Building Catch2 as an external library and linking to that. I was trying to compile my application and unit tests with address sanitizer enabled. Everything ran fine on various Linux platforms using gcc 11.4, but it failed on an Apple ARM64 platform using apple-clang 15.0. The reported asan error was AddressSanitizer: container-overflow (full stack trace at the end of the issue). Looking at the docs from this error, I believe this is a false positive along the lines of this one reported in spdlog since

If a part of the application is built with asan and another part is not instrumented, and both parts use e.g. instrumented std::vector, asan may report non-existent container overflow. This happens because instrumented and non-instrumented bits of std::vector, inlined and not, are mixed during linking, so you end up with incompletely instrumented std::vector

It also sounds like not all versions/classes in stdlib have been updated to support container overflow detection with address sanitizer, so I'm assuming that's why my other platforms work. If I either disable the container-overflow address sanitizer check or hack in compiling Catch2 with -fsanitize=address my unit tests run correctly.

So if I have correctly diagnosed the root cause correctly, it would seem very useful to have a flag to pass to cmake so that Catch2 itself can be compiled with address sanitizer enabled to easily prevent this false positive from being reported. I'm not completely confident in my assessment though since it would seem to me that running unit tests with sanitizers enabled should be relatively common, and I couldn't find anyone else reporting this issue.

I suppose this also could be a real bug in Catch::XmlWriter, but I did not dive into that code to check.

Full address sanitizer stack trace:

==16841==ERROR: AddressSanitizer: container-overflow on address 0x000142c37de8 at pc 0x000109b0307c bp 0x00016d082f90 sp 0x00016d082740
READ of size 7 at 0x000142c37de8 thread T0
    #0 0x109b03078 in wrap_memcpy+0x3fc (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x1b078)
    #1 0x1849c9818 in std::__1::basic_streambuf<char, std::__1::char_traits<char>>::xsputn(char const*, long)+0x54 (libc++.1.dylib:arm64e+0x23818)
    #2 0x102d8dbd8 in std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>> std::__1::__pad_and_output[abi:v160006]<char, std::__1::char_traits<char>>(std::__1::ostreambuf_iterator<char, std::__1::char_traits<char>>, char const*, char const*, char const*, std::__1::ios_base&, char)+0x2f4 (unittest:arm64+0x100015bd8)
    #3 0x102d8d610 in std::__1::basic_ostream<char, std::__1::char_traits<char>>& std::__1::__put_character_sequence[abi:v160006]<char, std::__1::char_traits<char>>(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, char const*, unsigned long)+0x228 (unittest:arm64+0x100015610)
    #4 0x10397983c in Catch::XmlWriter::endElement(Catch::XmlFormatting)+0x1ac (unittest:arm64+0x100c0183c)
    #5 0x1039798f0 in Catch::XmlWriter::ScopedElement::~ScopedElement()+0x1c (unittest:arm64+0x100c018f0)
    #6 0x103935c9c in Catch::JunitReporter::writeAssertion(Catch::AssertionStats const&)+0x568 (unittest:arm64+0x100bbdc9c)
    #7 0x103935338 in Catch::JunitReporter::writeSection(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, Catch::CumulativeReporterBase::SectionNode const&, bool)+0x420 (unittest:arm64+0x100bbd338)
    #8 0x103934e00 in Catch::JunitReporter::writeTestCase(Catch::CumulativeReporterBase::Node<Catch::TestCaseStats, Catch::CumulativeReporterBase::SectionNode> const&)+0x460 (unittest:arm64+0x100bbce00)
    #9 0x10393453c in Catch::JunitReporter::writeRun(Catch::CumulativeReporterBase::Node<Catch::TestRunStats, Catch::CumulativeReporterBase::Node<Catch::TestCaseStats, Catch::CumulativeReporterBase::SectionNode>> const&, double)+0x45c (unittest:arm64+0x100bbc53c)
    #10 0x1039340b8 in Catch::JunitReporter::testRunEndedCumulative()+0x20 (unittest:arm64+0x100bbc0b8)
    #11 0x103936a4c in Catch::MultiReporter::testRunEnded(Catch::TestRunStats const&)+0x30 (unittest:arm64+0x100bbea4c)
    #12 0x103969e28 in Catch::RunContext::~RunContext()+0x64 (unittest:arm64+0x100bf1e28)
    #13 0x103945dc4 in Catch::(anonymous namespace)::TestGroup::~TestGroup()+0x80 (unittest:arm64+0x100bcddc4)
    #14 0x103945308 in Catch::Session::runInternal()+0x8c8 (unittest:arm64+0x100bcd308)
    #15 0x1039449a0 in Catch::Session::run()+0x20 (unittest:arm64+0x100bcc9a0)
    #16 0x102d92f4c in main+0x43c (unittest:arm64+0x10001af4c)
    #17 0x1847110dc  (<unknown module>)
0x000142c37de8 is located 72 bytes inside of 96-byte region [0x000142c37da0,0x000142c37e00)
allocated by thread T0 here:
    #0 0x109b4978c in wrap__Znwm+0x74 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x6178c)
    #1 0x102e72c34 in void std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>>>::__push_back_slow_path<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&)+0x180 (unittest:arm64+0x1000fac34)
    #2 0x103979fc4 in Catch::XmlWriter::startElement(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, Catch::XmlFormatting)+0x144 (unittest:arm64+0x100c01fc4)
    #3 0x10397a0f8 in Catch::XmlWriter::scopedElement(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&, Catch::XmlFormatting)+0x18 (unittest:arm64+0x100c020f8)
    #4 0x1039343b8 in Catch::JunitReporter::writeRun(Catch::CumulativeReporterBase::Node<Catch::TestRunStats, Catch::CumulativeReporterBase::Node<Catch::TestCaseStats, Catch::CumulativeReporterBase::SectionNode>> const&, double)+0x2d8 (unittest:arm64+0x100bbc3b8)
    #5 0x1039340b8 in Catch::JunitReporter::testRunEndedCumulative()+0x20 (unittest:arm64+0x100bbc0b8)
    #6 0x103936a4c in Catch::MultiReporter::testRunEnded(Catch::TestRunStats const&)+0x30 (unittest:arm64+0x100bbea4c)
    #7 0x103969e28 in Catch::RunContext::~RunContext()+0x64 (unittest:arm64+0x100bf1e28)
    #8 0x103945dc4 in Catch::(anonymous namespace)::TestGroup::~TestGroup()+0x80 (unittest:arm64+0x100bcddc4)
    #9 0x103945308 in Catch::Session::runInternal()+0x8c8 (unittest:arm64+0x100bcd308)
    #10 0x1039449a0 in Catch::Session::run()+0x20 (unittest:arm64+0x100bcc9a0)
    #11 0x102d92f4c in main+0x43c (unittest:arm64+0x10001af4c)
    #12 0x1847110dc  (<unknown module>)
HINT: if you don't care about these errors you may set ASAN_OPTIONS=detect_container_overflow=0.
If you suspect a false positive see also: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow.
SUMMARY: AddressSanitizer: container-overflow (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x1b078) in wrap_memcpy+0x3fc
Shadow bytes around the buggy address:
  0x000142c37b00: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c37b80: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c37c00: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c37c80: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c37d00: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
=>0x000142c37d80: fa fa fa fa 00 00 00 00 00 00 00 00 00[fc]fc fc
  0x000142c37e00: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c37e80: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c37f00: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c37f80: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
  0x000142c38000: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd
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
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

1 participant