Menu

Segmentation fault with clang-16 parser

2023-07-12
2023-08-25
  • Andrey Alekseenko

    Trying to check a CPP file having a single #include <iostream> in it triggers a segfault when using Clang-16 parser.

    $ gdb --args ./bin/cppcheck --clang=/usr/bin/clang++-16 test.cpp 
    (gdb) r
    Starting program: /home/aland/cppcheck/build/bin/cppcheck --clang=/usr/bin/clang++-16 test.cpp
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    Checking test.cpp...
    [Detaching after vfork from child process 593957]
    
    Program received signal SIGSEGV, Segmentation fault.
    Variable::declarationId (this=0x55555762fc60) at lib/symboldatabase.h:318
    318                 return mNameToken->varId();
    (gdb) bt
    #0  Variable::declarationId (this=0x55555762fc60) at lib/symboldatabase.h:318
    #1  clangimport::Data::getVariableList (this=0x7fffffffb280) at lib/clangimport.cpp:285
    #2  clangimport::parseClangAstDump (tokenizer=tokenizer@entry=0x7fffffffb6f0, f=...) at /home/aland/cppcheck/lib/clangimport.cpp:1629
    #3  0x0000555555786a53 in CppCheck::check (this=0x7fffffffc880, path="test.cpp") at lib/cppcheck.cpp:546
    #4  0x0000555555636e09 in SingleExecutor::check (this=this@entry=0x7fffffffbca0) at cli/singleexecutor.cpp:60
    #5  0x0000555555623efc in CppCheckExecutor::check_internal (this=0x7fffffffd860, cppcheck=...) at cli/cppcheckexecutor.cpp:289
    #6  0x0000555555624fc3 in CppCheckExecutor::check (this=this@entry=0x7fffffffd860, argc=argc@entry=3, argv=argv@entry=0x7fffffffda18) at cli/cppcheckexecutor.cpp:223
    #7  0x0000555555609e02 in main (argc=3, argv=0x7fffffffda18) at cli/main.cpp:91
    

    A self-containing example triggering a similar segfault, but less reliably:

    template <typename, typename> class A { class b; };
    template <typename c, typename d> class A<c, d>::b { b(A &a); };
    template <typename c, typename d> A<c, d>::b::b(A &) {}
    
    $ /usr/bin/clang++-16 -fsyntax-only -Xclang -ast-dump -fno-color-diagnostics -x c++ test1.cpp
    TranslationUnitDecl 0x56037914f998 <<invalid sloc>> <invalid sloc>
    |-TypedefDecl 0x560379150200 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
    | `-BuiltinType 0x56037914ff60 '__int128'
    |-TypedefDecl 0x560379150270 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
    | `-BuiltinType 0x56037914ff80 'unsigned __int128'
    |-TypedefDecl 0x5603791505e8 <<invalid sloc>> <invalid sloc> implicit __NSConstantString '__NSConstantString_tag'
    | `-RecordType 0x560379150360 '__NSConstantString_tag'
    |   `-CXXRecord 0x5603791502c8 '__NSConstantString_tag'
    |-TypedefDecl 0x560379150680 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
    | `-PointerType 0x560379150640 'char *'
    |   `-BuiltinType 0x56037914fa40 'char'
    |-TypedefDecl 0x5603791968f8 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list '__va_list_tag[1]'
    | `-ConstantArrayType 0x5603791968a0 '__va_list_tag[1]' 1 
    |   `-RecordType 0x560379150770 '__va_list_tag'
    |     `-CXXRecord 0x5603791506d8 '__va_list_tag'
    |-ClassTemplateDecl 0x560379196b58 <test1.cpp:1:1, col:50> col:37 A
    | |-TemplateTypeParmDecl 0x560379196950 <col:11> col:19 typename depth 0 index 0
    | |-TemplateTypeParmDecl 0x5603791969f8 <col:21> col:29 typename depth 0 index 1
    | `-CXXRecordDecl 0x560379196ac8 <col:31, col:50> col:37 class A definition
    |   |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
    |   | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
    |   | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
    |   | |-MoveConstructor exists simple trivial needs_implicit
    |   | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
    |   | |-MoveAssignment exists simple trivial needs_implicit
    |   | `-Destructor simple irrelevant trivial needs_implicit
    |   |-CXXRecordDecl 0x560379196de0 <col:31, col:37> col:37 implicit referenced class A
    |   `-CXXRecordDecl 0x560379196e70 <col:41, col:47> col:47 class b
    |-CXXRecordDecl 0x560379197110 parent 0x560379196ac8 prev 0x560379196e70 <line:2:1, col:63> col:50 class b definition
    | |-DefinitionData empty standard_layout trivially_copyable has_user_declared_ctor can_const_default_init
    | | |-DefaultConstructor defaulted_is_constexpr
    | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
    | | |-MoveConstructor exists simple trivial needs_implicit
    | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
    | | |-MoveAssignment exists simple trivial needs_implicit
    | | `-Destructor simple irrelevant trivial needs_implicit
    | |-CXXRecordDecl 0x560379197250 <col:35, col:50> col:50 implicit referenced class b
    | `-CXXConstructorDecl 0x5603791974b8 <col:54, col:60> col:54 b 'void (A<type-parameter-0-0, type-parameter-0-1> &)'
    |   `-ParmVarDecl 0x560379197380 <col:56, col:59> col:59 a 'A<type-parameter-0-0, type-parameter-0-1> &'
    `-CXXConstructorDecl 0x5603791b5600 parent 0x560379197110 prev 0x5603791974b8 <line:3:1, col:55> col:47 b 'void (A<type-parameter-0-0, type-parameter-0-1> &)'
      |-ParmVarDecl 0x5603791b5570 <col:49, col:51> col:52 'A<type-parameter-0-0, type-parameter-0-1> &'
      `-CompoundStmt 0x5603791b5700 <col:54, col:55>
    

    Tested with CppCheck 3508464c6dadc42162cb618ece6b69763df44b23, Ubuntu clang version 16.0.6

     

    Last edit: Andrey Alekseenko 2023-07-12
  • Eric Johnson

    Eric Johnson - 2023-08-24

    I can also reproduce this with clang 15.0.7 (Fedora 15.0.7-2.fc37) on CppCheck 5a7c7b9b5a17a8fcd212e7b77f6b5777b6c34479 (current git main). AddressSanitizer says it's a use-after-free:

    $ cppcheck/build/bin/cppcheck --clang=/usr/bin/clang++-15 --cppcheck-build-dir=cppcheck_build_dir test1.cpp
    Checking test1.cpp...
    =================================================================
    ==1021405==ERROR: AddressSanitizer: heap-use-after-free on address 0x60c000006dc0 at pc 0x000000da8b5b bp 0x7ffdf6182690 sp 0x7ffdf6182688
    READ of size 8 at 0x60c000006dc0 thread T0
        #0 0xda8b5a in Variable::declarationId() const /tmp/cppcheck_test/cppcheck/lib/symboldatabase.h:302
        #1 0x12e551e in clangimport::Data::getVariableList() const /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:284
        #2 0x12df477 in clangimport::parseClangAstDump(Tokenizer*, std::istream&) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:1628
        #3 0x134b94d in CppCheck::check(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /tmp/cppcheck_test/cppcheck/lib/cppcheck.cpp:542
        #4 0xd2e88d in SingleExecutor::check() /tmp/cppcheck_test/cppcheck/cli/singleexecutor.cpp:57
        #5 0xc9fdbb in CppCheckExecutor::check_internal(CppCheck&) /tmp/cppcheck_test/cppcheck/cli/cppcheckexecutor.cpp:303
        #6 0xc9ecba in CppCheckExecutor::check(int, char const* const*) /tmp/cppcheck_test/cppcheck/cli/cppcheckexecutor.cpp:237
        #7 0xc5f780 in main /tmp/cppcheck_test/cppcheck/cli/main.cpp:91
        #8 0x7f7839e4a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
        #9 0x7f7839e4a5c8 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x275c8)
        #10 0xc5f5f4 in _start (/tmp/cppcheck_test/cppcheck/build/bin/cppcheck+0xc5f5f4)
    
    0x60c000006dc0 is located 0 bytes inside of 120-byte region [0x60c000006dc0,0x60c000006e38)
    freed by thread T0 here:
        #0 0x7f783a4bbbe8 in operator delete(void*) (/lib64/libasan.so.8+0xbbbe8)
        #1 0x1148429 in std::__new_allocator<Variable>::deallocate(Variable*, unsigned long) /usr/include/c++/12/bits/new_allocator.h:158
        #2 0x1147397 in std::allocator_traits<std::allocator<Variable> >::deallocate(std::allocator<Variable>&, Variable*, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:496
        #3 0x114364b in std::__cxx1998::_Vector_base<Variable, std::allocator<Variable> >::_M_deallocate(Variable*, unsigned long) /usr/include/c++/12/bits/stl_vector.h:387
        #4 0x1303c29 in std::__cxx1998::vector<Variable, std::allocator<Variable> >::reserve(unsigned long) (/tmp/cppcheck_test/cppcheck/build/bin/cppcheck+0x1303c29)
        #5 0x12f3248 in std::__debug::vector<Variable, std::allocator<Variable> >::reserve(unsigned long) /usr/include/c++/12/debug/vector:430
        #6 0x12d92ff in clangimport::AstNode::createTokensFunctionDecl(TokenList*) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:1376
        #7 0x12ccfc4 in clangimport::AstNode::createTokens(TokenList*) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:837
        #8 0x12e67cd in clangimport::AstNode::createTokens1(TokenList*) (/tmp/cppcheck_test/cppcheck/build/bin/cppcheck+0x12e67cd)
        #9 0x12df245 in clangimport::parseClangAstDump(Tokenizer*, std::istream&) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:1618
        #10 0x134b94d in CppCheck::check(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /tmp/cppcheck_test/cppcheck/lib/cppcheck.cpp:542
        #11 0xd2e88d in SingleExecutor::check() /tmp/cppcheck_test/cppcheck/cli/singleexecutor.cpp:57
        #12 0xc9fdbb in CppCheckExecutor::check_internal(CppCheck&) /tmp/cppcheck_test/cppcheck/cli/cppcheckexecutor.cpp:303
        #13 0xc9ecba in CppCheckExecutor::check(int, char const* const*) /tmp/cppcheck_test/cppcheck/cli/cppcheckexecutor.cpp:237
        #14 0xc5f780 in main /tmp/cppcheck_test/cppcheck/cli/main.cpp:91
        #15 0x7f7839e4a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
    
    previously allocated by thread T0 here:
        #0 0x7f783a4bb1a8 in operator new(unsigned long) (/lib64/libasan.so.8+0xbb1a8)
        #1 0x11491c5 in std::__new_allocator<Variable>::allocate(unsigned long, void const*) /usr/include/c++/12/bits/new_allocator.h:137
        #2 0x1148407 in std::allocator_traits<std::allocator<Variable> >::allocate(std::allocator<Variable>&, unsigned long) /usr/include/c++/12/bits/alloc_traits.h:464
        #3 0x1147363 in std::__cxx1998::_Vector_base<Variable, std::allocator<Variable> >::_M_allocate(unsigned long) /usr/include/c++/12/bits/stl_vector.h:378
        #4 0x131cb4d in Variable* std::__cxx1998::vector<Variable, std::allocator<Variable> >::_M_allocate_and_copy<Variable const*>(unsigned long, Variable const*, Variable const*) /usr/include/c++/12/bits/stl_vector.h:1614
        #5 0x1303b2a in std::__cxx1998::vector<Variable, std::allocator<Variable> >::reserve(unsigned long) (/tmp/cppcheck_test/cppcheck/build/bin/cppcheck+0x1303b2a)
        #6 0x12f3248 in std::__debug::vector<Variable, std::allocator<Variable> >::reserve(unsigned long) /usr/include/c++/12/debug/vector:430
        #7 0x12d92ff in clangimport::AstNode::createTokensFunctionDecl(TokenList*) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:1376
        #8 0x12ccfc4 in clangimport::AstNode::createTokens(TokenList*) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:837
        #9 0x12c925b in clangimport::AstNode::createScope(TokenList*, Scope::ScopeType, std::__debug::vector<std::shared_ptr<clangimport::AstNode>, std::allocator<std::shared_ptr<clangimport::AstNode> > > const&, Token const*) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:700
        #10 0x12db6fa in clangimport::AstNode::createTokensForCXXRecord(TokenList*) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:1461
        #11 0x12ce679 in clangimport::AstNode::createTokens(TokenList*) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:918
        #12 0x12e67cd in clangimport::AstNode::createTokens1(TokenList*) (/tmp/cppcheck_test/cppcheck/build/bin/cppcheck+0x12e67cd)
        #13 0x12dee0b in clangimport::parseClangAstDump(Tokenizer*, std::istream&) /tmp/cppcheck_test/cppcheck/lib/clangimport.cpp:1599
        #14 0x134b94d in CppCheck::check(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /tmp/cppcheck_test/cppcheck/lib/cppcheck.cpp:542
        #15 0xd2e88d in SingleExecutor::check() /tmp/cppcheck_test/cppcheck/cli/singleexecutor.cpp:57
        #16 0xc9fdbb in CppCheckExecutor::check_internal(CppCheck&) /tmp/cppcheck_test/cppcheck/cli/cppcheckexecutor.cpp:303
        #17 0xc9ecba in CppCheckExecutor::check(int, char const* const*) /tmp/cppcheck_test/cppcheck/cli/cppcheckexecutor.cpp:237
        #18 0xc5f780 in main /tmp/cppcheck_test/cppcheck/cli/main.cpp:91
        #19 0x7f7839e4a50f in __libc_start_call_main (/lib64/libc.so.6+0x2750f)
    
    SUMMARY: AddressSanitizer: heap-use-after-free /tmp/cppcheck_test/cppcheck/lib/symboldatabase.h:302 in Variable::declarationId() const
    Shadow bytes around the buggy address:
      0x0c187fff8d60: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
      0x0c187fff8d70: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
      0x0c187fff8d80: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
      0x0c187fff8d90: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
      0x0c187fff8da0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
    =>0x0c187fff8db0: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
      0x0c187fff8dc0: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
      0x0c187fff8dd0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
      0x0c187fff8de0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
      0x0c187fff8df0: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
      0x0c187fff8e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    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
    ==1021405==ABORTING
    
     
  • CHR

    CHR - 2023-08-24

    WAG: https://github.com/danmar/cppcheck/commit/d81a758850b98c7b7114e6ed89e5245ed4a52b15 changed the type of argumentList from list to vector, so this will dangle after reallocation:
    mData->varDecl(addr, vartok, &function->argumentList.back());

     
  • CHR

    CHR - 2023-08-25

    Fix proposed here: https://github.com/danmar/cppcheck/pull/5367
    Out of curiousity, what is your reason to use --clang, and how does it work in practice?

     

    Last edit: CHR 2023-08-25

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.