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

Supplying own command line options by following the documentation results in UB (?) #2811

Closed
meator opened this issue Feb 11, 2024 · 3 comments

Comments

@meator
Copy link

meator commented Feb 11, 2024

Describe the bug
The code sample in https://github.com/catchorg/Catch2/blob/devel/docs/own-main.md#adding-your-own-command-line-options very likely results in undefined behavior.

Expected behavior
Everything working.

Reproduction steps
Here is the code triggering this:

Adding your own command line options code sample
#include <catch2/catch_session.hpp>

#include <iostream>

int main( int argc, char* argv[] ) {
  Catch::Session session; // There must be exactly one instance

  int height = 0; // Some user variable you want to be able to set

  // Build a new parser on top of Catch2's
  using namespace Catch::Clara;
  auto cli
    = session.cli()           // Get Catch2's command line parser
    | Opt( height, "height" ) // bind variable to a new option, with a hint string
        ["-g"]["--height"]    // the option names it will respond to
        ("how high?");        // description string for the help output

  // Now pass the new composite back to Catch2 so it uses that
  session.cli( cli );

  // Let Catch2 (using Clara) parse the command line
  int returnCode = session.applyCommandLine( argc, argv );
  if( returnCode != 0 ) // Indicates a command line error
      return returnCode;

  // if set on the command line then 'height' is now set at this point
  if( height > 0 )
      std::cout << "height: " << height << std::endl;

  return session.run();
}

As you can see, this is a copy of the example given in documentation with appropriate header files added.

  1. Get v3.5.2 source archive and compile it
    I have tried configuring Catch2 with many flags and compiler flags for testing, but this bug is reproducible with simple cmake ..
  2. Run this
    g++ -ICatch2-3.5.2/src -ICatch2-3.5.2/build/generated-includes -D_GLIBCXX_DEBUG=1 main.cc Catch2-3.5.2/build/src/libCatch2.a
    
  3. Run the executable

I did a lot of testing and got a lot of different error messages. I have tried to throw ASAN and GLIBCXX_DEBUG into this. Here are some error messages:

/usr/include/c++/13.2/bits/new_allocator.h:187:4: runtime error: store to misaligned address 0x000000000122 for type 'struct StringRef', which requires 8 byte alignment
0x000000000122: note: pointer points here
<memory cannot be printed>
AddressSanitizer:DEADLYSIGNAL
=================================================================
==2337==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000122 (pc 0x56237c8324ae bp 0x7fffc7dc1680 sp 0x7fffc7dc1630 T0)
==2337==The signal is caused by a WRITE memory access.
==2337==Hint: address points to the zero page.
    #0 0x56237c8324ae in std::__cxx1998::vector<Catch::StringRef, std::allocator<Catch::StringRef> >::push_back(Catch::StringRef const&) (/home/meator/bigfiles/catch/a.out+0x5b4ae) (BuildId: bf4e1d8717afa4228672aac10713151c3c9de931)
    #1 0x56237c82d108 in std::__debug::vector<Catch::StringRef, std::allocator<Catch::StringRef> >::push_back(Catch::StringRef const&) (/home/meator/bigfiles/catch/a.out+0x56108) (BuildId: bf4e1d8717afa4228672aac10713151c3c9de931)
    #2 0x56237c82a360 in Catch::Clara::Opt::operator[](Catch::StringRef) && (/home/meator/bigfiles/catch/a.out+0x53360) (BuildId: bf4e1d8717afa4228672aac10713151c3c9de931)
    #3 0x56237c87109d in Catch::makeCommandLineParser(Catch::ConfigData&) src/catch2/internal/catch_commandline.cpp:306
    #4 0x56237c84c2f6 in Catch::Session::Session() src/catch2/catch_session.cpp:180
    #5 0x56237c826de3 in main /home/meator/bigfiles/catch/main.cpp:6
    #6 0x7f2208a27c4b in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #7 0x7f2208a27d04 in __libc_start_main_impl ../csu/libc-start.c:360
    #8 0x56237c826be0 in _start ../sysdeps/x86_64/start.S:115

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/home/meator/bigfiles/catch/a.out+0x5b4ae) (BuildId: bf4e1d8717afa4228672aac10713151c3c9de931) in std::__cxx1998::vector<Catch::StringRef, std::allocator<Catch::StringRef> >::push_back(Catch::StringRef const&)
==2337==ABORTING
/usr/include/c++/13.2/bits/stl_construct.h:151:22: runtime error: member call on address 0x7ffd8427f330 which does not point to an object of type 'Arg'
0x7ffd8427f330: note: object is of type 'Catch::Clara::Opt'
 79 7f 00 00  58 55 7f cc f6 55 00 00  00 00 00 00 00 00 00 00  90 08 00 00 30 60 00 00  80 08 00 00
              ^~~~~~~~~~~~~~~~~~~~~~~
              vptr for 'Catch::Clara::Opt'
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3042==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000024 (pc 0x7f79c8108773 bp 0x7ffd8427f370 sp 0x7ffd8427e640 T0)
==3042==The signal is caused by a READ memory access.
==3042==Hint: address points to the zero page.
    #0 0x7f79c8108773 in __gnu_debug::_Safe_sequence_base::_M_detach_all() (/usr/lib/libstdc++.so.6+0x108773) (BuildId: 009f6979edcb30d11a93778e994468a90a431e93)
    #1 0x55f6cc73d7c1 in __gnu_debug::_Safe_sequence_base::~_Safe_sequence_base() /usr/include/c++/13.2/debug/safe_base.h:221
    #2 0x55f6cc73eb8d in __gnu_debug::_Safe_sequence<std::__debug::vector<Catch::StringRef, std::allocator<Catch::StringRef> > >::~_Safe_sequence() /usr/include/c++/13.2/debug/safe_sequence.h:108
    #3 0x55f6cc73ec23 in __gnu_debug::_Safe_container<std::__debug::vector<Catch::StringRef, std::allocator<Catch::StringRef> >, std::allocator<Catch::StringRef>, __gnu_debug::_Safe_sequence, true>::~_Safe_container() /usr/include/c++/13.2/debug/safe_container.h:41
    #4 0x55f6cc73ed3a in std::__debug::vector<Catch::StringRef, std::allocator<Catch::StringRef> >::~vector() /usr/include/c++/13.2/debug/vector:230
    #5 0x55f6cc73ee50 in Catch::Clara::Opt::~Opt() Catch2-3.5.2/src/catch2/internal/catch_clara.hpp:570
    #6 0x55f6cc75600f in void std::_Destroy<Catch::Clara::Arg>(Catch::Clara::Arg*) /usr/include/c++/13.2/bits/stl_construct.h:151
    #7 0x55f6cc752c40 in void std::_Destroy_aux<false>::__destroy<Catch::Clara::Arg*>(Catch::Clara::Arg*, Catch::Clara::Arg*) /usr/include/c++/13.2/bits/stl_construct.h:163
    #8 0x55f6cc74d8d3 in void std::_Destroy<Catch::Clara::Arg*>(Catch::Clara::Arg*, Catch::Clara::Arg*) /usr/include/c++/13.2/bits/stl_construct.h:196
    #9 0x55f6cc74471f in void std::_Destroy<Catch::Clara::Arg*, Catch::Clara::Arg>(Catch::Clara::Arg*, Catch::Clara::Arg*, std::allocator<Catch::Clara::Arg>&) /usr/include/c++/13.2/bits/alloc_traits.h:947
    #10 0x55f6cc74471f in std::__cxx1998::vector<Catch::Clara::Arg, std::allocator<Catch::Clara::Arg> >::~vector() /usr/include/c++/13.2/bits/stl_vector.h:732
    #11 0x55f6cc73fa65 in std::__debug::vector<Catch::Clara::Arg, std::allocator<Catch::Clara::Arg> >::~vector() /usr/include/c++/13.2/debug/vector:230
    #12 0x55f6cc73fbd4 in Catch::Clara::Parser::~Parser() Catch2-3.5.2/src/catch2/internal/catch_clara.hpp:646
    #13 0x55f6cc779e5d in Catch::Clara::Parser Catch::Clara::Detail::ComposableParserImpl<Catch::Clara::ExeName>::operator|<Catch::Clara::Help>(Catch::Clara::Help const&) const (/home/meator/bigfiles/catch/a.out+0x93e5d) (BuildId: 24fc88333d77e6c95da84058b281493fdeddb1b1)
    #14 0x55f6cc7750ee in Catch::makeCommandLineParser(Catch::ConfigData&) (/home/meator/bigfiles/catch/a.out+0x8f0ee) (BuildId: 24fc88333d77e6c95da84058b281493fdeddb1b1)
    #15 0x55f6cc761c1e in Catch::Session::Session() (/home/meator/bigfiles/catch/a.out+0x7bc1e) (BuildId: 24fc88333d77e6c95da84058b281493fdeddb1b1)
    #16 0x55f6cc73ae53 in main /home/meator/bigfiles/catch/main.cpp:6
    #17 0x7f79c7427c4b in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #18 0x7f79c7427d04 in __libc_start_main_impl ../csu/libc-start.c:360
    #19 0x55f6cc73ac50 in _start ../sysdeps/x86_64/start.S:115

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/usr/lib/libstdc++.so.6+0x108773) (BuildId: 009f6979edcb30d11a93778e994468a90a431e93) in __gnu_debug::_Safe_sequence_base::_M_detach_all()
==3042==ABORTING

This is with v3.4.0:

terminate called after throwing an instance of 'std::bad_array_new_length'
  what():  std::bad_array_new_length
zsh: IOT instruction  ./tests/ttest

Platform information:

  • OS: Void Linux
  • Compiler+version: GCC v13.2.0
  • Catch version: v3.4.0 and v3.5.2 (latest)

Additional context
This does not trigger when compiling the sample and specifying Catch2 as a dependency with the Meson build system and with CMake integration. The CMakeLists.txt file I've used to verify this claim can be found here:

CMakeLists.txt
cmake_minimum_required(VERSION 3.16)

project(test)

Include(FetchContent)

FetchContent_Declare(
  Catch2
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG        v3.5.2
  GIT_SHALLOW    true
)

FetchContent_MakeAvailable(Catch2)

# These tests need their own main
add_executable(test test.cpp)
target_link_libraries(test PRIVATE Catch2::Catch2)

This doesn't trigger with any of the integration options, it happens only when Catch2 is built separately and the target program is compiled with it. This was very hard to debug by the way. I ran into this because I was seeing crashing when compiling with system Catch2 and no crashing when compiling with Meson dependency.

@meator
Copy link
Author

meator commented Feb 12, 2024

To summarize, using system installed Catch2, _GLIBCXX_DEBUG=1 and the sample code crashes.

@sketch34
Copy link

sketch34 commented Apr 18, 2024

using system installed Catch2

IIRC all code including any libraries must be compiled with _GLIBCXX_DEBUG=1. If your code is compiled with _GLIBCXX_DEBUG=1 and some of its dependencies are not then crashes ensue.

From https://gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html:

Note that this flag changes the sizes and behavior of standard class templates such as std::vector, and therefore you can only link code compiled with debug mode and code compiled without debug mode if no instantiation of a container is passed between the two translation units.

So it will work only if there is no passing of any std containers between code in TUs that were compiled with different _GLIBCXX_DEBUG values.

@meator
Copy link
Author

meator commented May 2, 2024

Oh, thanks for the explanation. I'm dumb. Now that you've pointed it out, it makes a lot of sense that mixing _GLIBCXX_DEBUG and non-_GLIBCXX_DEBUG code will break havoc.

@meator meator closed this as completed May 2, 2024
meator added a commit to enkore/j4-dmenu-desktop that referenced this issue May 3, 2024
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