Skip to content

Commit 7d5a54c

Browse files
Fix #14877: heap-use-after-free in Tokenizer::simplifyUsing() (#8679)
In a large codebase, constructs like `using C = struct C { C() {} };` lead to errors such as `Code.cpp:0:0: error: Bailing out from analysis: Checking file failed: out of memory [internalError]`. When compiling cppcheck using clang 22's address sanitizer, the analysis terminates with the following messages: ``` ================================================================= ==1390963==ERROR: AddressSanitizer: heap-use-after-free on address 0x7c985f8ff900 at pc 0x55dcbe530972 bp 0x7ffd79cf5010 sp 0x7ffd79cf5008 READ of size 8 at 0x7c985f8ff900 thread T0 #0 0x55dcbe530971 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::size() const /usr/include/c++/bits/basic_string.h:1165:19 #1 0x55dcbe53c624 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::length() const /usr/include/c++/bits/basic_string.h:1176:16 #2 0x55dcbe54f8d6 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /usr/include/c++/bits/basic_string.tcc:313:36 #3 0x55dcbe54f7f0 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>::assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) /usr/include/c++/bits/basic_string.h:1771:8 #4 0x55dcbe54f7bc in 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>> const&) /usr/include/c++/bits/basic_string.h:906:15 #5 0x55dcbe635d19 in Tokenizer::simplifyUsing() ./src/cppcheck/lib/tokenize.cpp:3214:32 #6 0x55dcbe6470b3 in Tokenizer::simplifyTokenList1(char const*) ./src/cppcheck/lib/tokenize.cpp:5910:12 #7 0x55dcbe643cb8 in Tokenizer::simplifyTokens1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, int) ./src/cppcheck/lib/tokenize.cpp:3527:14 #8 0x55dcbedb37e7 in CppCheck::checkInternal(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::function<simplecpp::TokenList (std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>&, std::__cxx11::list<simplecpp::Output, std::allocator<simplecpp::Output>>*)> const&) ./src/cppcheck/lib/cppcheck.cpp:1203:32 #9 0x55dcbeda7bf4 in CppCheck::checkFile(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) ./src/cppcheck/lib/cppcheck.cpp:898:12 #10 0x55dcbeda7862 in CppCheck::check(FileWithDetails const&) ./src/cppcheck/lib/cppcheck.cpp:802:23 #11 0x55dcbf294ce0 in SingleExecutor::check() ./src/cppcheck/cli/singleexecutor.cpp:52:29 #12 0x55dcbf23cf66 in CppCheckExecutor::check_internal(Settings const&, Suppressions&) const ./src/cppcheck/cli/cppcheckexecutor.cpp:453:32 #13 0x55dcbf23c4fc in CppCheckExecutor::check_wrapper(Settings const&, Suppressions&) ./src/cppcheck/cli/cppcheckexecutor.cpp:295:12 #14 0x55dcbf23c1af in CppCheckExecutor::check(int, char const* const*) ./src/cppcheck/cli/cppcheckexecutor.cpp:280:21 #15 0x55dcbf23b67d in main ./src/cppcheck/cli/main.cpp:71:17 #16 0x7fe8606517e4 in __libc_start_main (/lib64/libc.so.6+0x3a7e4) (BuildId: b81415c1738806b536fb1599d7af2d15bf6a86b7) #17 0x55dcbe317bed in _start (./src/cppcheck/build/bin/cppcheck+0x4bbbed) 0x7c985f8ff900 is located 32 bytes inside of 112-byte region [0x7c985f8ff8e0,0x7c985f8ff950) freed by thread T0 here: #0 0x55dcbe4642ba in operator delete(void*) /usr/local/src/conda/compiler-rt-packages-22.1.8/compiler-rt/lib/asan/asan_new_delete.cpp:177:44 #1 0x55dcbf144bd0 in Token::deleteNext(int) ./src/cppcheck/lib/token.cpp:281:9 #2 0x55dcbf145e2d in Token::deleteThis() ./src/cppcheck/lib/token.cpp:360:9 #3 0x55dcbe634c84 in Tokenizer::simplifyUsing() ./src/cppcheck/lib/tokenize.cpp:3086:18 #4 0x55dcbe6470b3 in Tokenizer::simplifyTokenList1(char const*) ./src/cppcheck/lib/tokenize.cpp:5910:12 #5 0x55dcbe643cb8 in Tokenizer::simplifyTokens1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, int) ./src/cppcheck/lib/tokenize.cpp:3527:14 #6 0x55dcbedb37e7 in CppCheck::checkInternal(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::function<simplecpp::TokenList (std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>&, std::__cxx11::list<simplecpp::Output, std::allocator<simplecpp::Output>>*)> const&) ./src/cppcheck/lib/cppcheck.cpp:1203:32 #7 0x55dcbeda7bf4 in CppCheck::checkFile(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) ./src/cppcheck/lib/cppcheck.cpp:898:12 #8 0x55dcbeda7862 in CppCheck::check(FileWithDetails const&) ./src/cppcheck/lib/cppcheck.cpp:802:23 #9 0x55dcbf294ce0 in SingleExecutor::check() ./src/cppcheck/cli/singleexecutor.cpp:52:29 #10 0x55dcbf23cf66 in CppCheckExecutor::check_internal(Settings const&, Suppressions&) const ./src/cppcheck/cli/cppcheckexecutor.cpp:453:32 #11 0x55dcbf23c4fc in CppCheckExecutor::check_wrapper(Settings const&, Suppressions&) ./src/cppcheck/cli/cppcheckexecutor.cpp:295:12 #12 0x55dcbf23c1af in CppCheckExecutor::check(int, char const* const*) ./src/cppcheck/cli/cppcheckexecutor.cpp:280:21 #13 0x55dcbf23b67d in main ./src/cppcheck/cli/main.cpp:71:17 #14 0x7fe8606517e4 in __libc_start_main (/lib64/libc.so.6+0x3a7e4) (BuildId: b81415c1738806b536fb1599d7af2d15bf6a86b7) previously allocated by thread T0 here: #0 0x55dcbe4638aa in operator new(unsigned long) /usr/local/src/conda/compiler-rt-packages-22.1.8/compiler-rt/lib/asan/asan_new_delete.cpp:109:35 #1 0x55dcbf14fb4a in Token::insertToken(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, bool) ./src/cppcheck/lib/token.cpp:1069:20 #2 0x55dcbe758b2e in Token::insertToken(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) ./src/cppcheck/lib/token.h:991:16 #3 0x55dcbf18de6e in TokenList::createTokens(simplecpp::TokenList&&) ./src/cppcheck/lib/tokenlist.cpp:392:37 #4 0x55dcbedc8c03 in CppCheck::checkInternal(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::function<simplecpp::TokenList (std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>&, std::__cxx11::list<simplecpp::Output, std::allocator<simplecpp::Output>>*)> const&)::$_2::operator()() const ./src/cppcheck/lib/cppcheck.cpp:1157:35 #5 0x55dcbedb9322 in void Timer::run<CppCheck::checkInternal(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::function<simplecpp::TokenList (std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>&, std::__cxx11::list<simplecpp::Output, std::allocator<simplecpp::Output>>*)> const&)::$_2>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, TimerResultsIntf*, CppCheck::checkInternal(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::function<simplecpp::TokenList (std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>&, std::__cxx11::list<simplecpp::Output, std::allocator<simplecpp::Output>>*)> const&)::$_2 const&) ./src/cppcheck/lib/timer.h:77:9 #6 0x55dcbedb2f65 in CppCheck::checkInternal(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, std::function<simplecpp::TokenList (std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>>>&, std::__cxx11::list<simplecpp::Output, std::allocator<simplecpp::Output>>*)> const&) ./src/cppcheck/lib/cppcheck.cpp:1152:17 #7 0x55dcbeda7bf4 in CppCheck::checkFile(FileWithDetails const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&) ./src/cppcheck/lib/cppcheck.cpp:898:12 #8 0x55dcbeda7862 in CppCheck::check(FileWithDetails const&) ./src/cppcheck/lib/cppcheck.cpp:802:23 #9 0x55dcbf294ce0 in SingleExecutor::check() ./src/cppcheck/cli/singleexecutor.cpp:52:29 #10 0x55dcbf23cf66 in CppCheckExecutor::check_internal(Settings const&, Suppressions&) const ./src/cppcheck/cli/cppcheckexecutor.cpp:453:32 #11 0x55dcbf23c4fc in CppCheckExecutor::check_wrapper(Settings const&, Suppressions&) ./src/cppcheck/cli/cppcheckexecutor.cpp:295:12 #12 0x55dcbf23c1af in CppCheckExecutor::check(int, char const* const*) ./src/cppcheck/cli/cppcheckexecutor.cpp:280:21 #13 0x55dcbf23b67d in main ./src/cppcheck/cli/main.cpp:71:17 #14 0x7fe8606517e4 in __libc_start_main (/lib64/libc.so.6+0x3a7e4) (BuildId: b81415c1738806b536fb1599d7af2d15bf6a86b7) SUMMARY: AddressSanitizer: heap-use-after-free ./src/cppcheck/lib/tokenize.cpp:3214:32 in Tokenizer::simplifyUsing() Shadow bytes around the buggy address: 0x7c985f8ff680: fd fa fa fa fa fa fa fa fa fa fd fd fd fd fd fd 0x7c985f8ff700: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa 0x7c985f8ff780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fa fa 0x7c985f8ff800: fa fa fa fa fa fa 00 00 00 00 00 00 00 00 00 00 0x7c985f8ff880: 00 00 00 fa fa fa fa fa fa fa fa fa fd fd fd fd =>0x7c985f8ff900:[fd]fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa 0x7c985f8ff980: fa fa fd fd fd fd fd fd fd fd fd fd fd fd fd fd 0x7c985f8ffa00: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd 0x7c985f8ffa80: fd fd fd fd fd fd fa fa fa fa fa fa fa fa 00 00 0x7c985f8ffb00: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa 0x7c985f8ffb80: fa fa fa fa 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 ==1390963==ABORTING ``` The proposed fix avoids storing a reference to a memory area that will eventually be deallocated before the reference is used.
1 parent 86f4c91 commit 7d5a54c

3 files changed

Lines changed: 10 additions & 3 deletions

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ Gerhard Zlabinger
156156
Gerik Rhoden
157157
Gianfranco Costamagna
158158
Gianluca Scacco
159+
Gilmar Santos Jr
159160
Gleydson Soares
160161
Goncalo Mao-Cheia
161162
Goran Džaferi

lib/tokenize.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,7 +3015,6 @@ bool Tokenizer::simplifyUsing()
30153015
Token::Match(tok->linkAt(2), "] ] = ::| %name%")))))
30163016
continue;
30173017

3018-
const std::string& name = tok->strAt(1);
30193018
const Token *nameToken = tok->next();
30203019
std::string scope = currentScope->fullName;
30213020
Token *usingStart = tok;
@@ -3064,7 +3063,7 @@ bool Tokenizer::simplifyUsing()
30643063
if (!hasName) {
30653064
std::string newName;
30663065
if (structEnd->strAt(2) == ";")
3067-
newName = name;
3066+
newName = nameToken->str();
30683067
else
30693068
newName = "Unnamed" + std::to_string(mUnnamedCount++);
30703069
TokenList::copyTokens(structEnd->next(), tok, start);
@@ -3211,7 +3210,7 @@ bool Tokenizer::simplifyUsing()
32113210
if (!isTypedefInfoAdded && Token::Match(tok1, "%name% (")) {
32123211
isTypedefInfoAdded = true;
32133212
TypedefInfo usingInfo;
3214-
usingInfo.name = name;
3213+
usingInfo.name = nameToken->str();
32153214
usingInfo.filename = list.file(nameToken);
32163215
usingInfo.lineNumber = nameToken->linenr();
32173216
usingInfo.column = nameToken->column();

test/testsimplifyusing.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class TestSimplifyUsing : public TestFixture {
9898
TEST_CASE(simplifyUsing10335);
9999
TEST_CASE(simplifyUsing10720);
100100
TEST_CASE(simplifyUsing13873); // function declaration
101+
TEST_CASE(simplifyUsing14877);
101102

102103
TEST_CASE(scopeInfo1);
103104
TEST_CASE(scopeInfo2);
@@ -1667,6 +1668,12 @@ class TestSimplifyUsing : public TestFixture {
16671668
ASSERT_EQUALS("namespace NS1 { void * f ( ) ; }", tok(code3));
16681669
}
16691670

1671+
void simplifyUsing14877() {
1672+
const char code[] = "using C = struct C { C() {} };";
1673+
const char expected[] = "struct C { C ( ) { } } ;";
1674+
ASSERT_EQUALS(expected, tok(code));
1675+
}
1676+
16701677
void scopeInfo1() {
16711678
const char code[] = "struct A {\n"
16721679
" enum class Mode { UNKNOWN, ENABLED, NONE, };\n"

0 commit comments

Comments
 (0)