From d7d165a0d2f320cbc060efe6270e4b4fb2f617fb Mon Sep 17 00:00:00 2001 From: Jeewoong Kim Date: Sat, 5 Sep 2026 08:21:30 +0900 Subject: [PATCH] Fix buffer size value flow for global new arrays --- lib/valueflow.cpp | 71 ++++++++++++++++++++++++++++++++++++++ test/testbufferoverrun.cpp | 23 ++++++++++++ 2 files changed, 94 insertions(+) diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 325a55e98e5..d3cb671d3c9 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -7064,6 +7064,77 @@ static void valueFlowDynamicBufferSize(const TokenList& tokenlist, const SymbolD return sizeValue; }; + std::map globalBufferSizes; + + // Get buffer sizes for global pointers initialized with new + for (const Variable* var : symboldatabase.variableList()) { + if (!var || !var->isGlobal() || !var->isPointer() || var->isExtern()) + continue; + + const Token* nameTok = var->nameToken(); + if (!Token::Match(nameTok, "%var% ; %var% =")) + continue; + + const Token* initLhs = nameTok->tokAt(2); + if (!initLhs || initLhs->variable() != var) + continue; + + const Token* assignTok = initLhs->next(); + const Token* rhs = assignTok->astOperand2(); + while (rhs && rhs->isCast()) + rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); + + if (!rhs || !rhs->isCpp() || rhs->str() != "new") + continue; + + const MathLib::bigint sizeValue = getBufferSizeFromNew(rhs); + if (sizeValue < 0) + continue; + + ValueFlow::Value value(sizeValue); + value.errorPath.emplace_back(assignTok, "Assign " + initLhs->str() + ", buffer with size " + MathLib::toString(sizeValue)); + value.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; + value.setKnown(); + globalBufferSizes.emplace(var, std::move(value)); + } + + // Remove initial buffer sizes if the pointers are changed later + for (const Token* tok = tokenlist.front(); tok && !globalBufferSizes.empty(); tok = tok->next()) { + const Variable* var = tok->variable(); + if (!var) + continue; + + const auto it = globalBufferSizes.find(var); + if (it == globalBufferSizes.end()) + continue; + + const Token* nameTok = var->nameToken(); + const Token* initLhs = Token::Match(nameTok, "%var% ; %var% =") ? nameTok->tokAt(2) : nullptr; + if (tok == nameTok || tok == initLhs) + continue; + + if (isVariableChanged(tok, 0, settings)) + globalBufferSizes.erase(it); + } + + // Propagate the buffer size through main() + for (const auto& entry : globalBufferSizes) { + const Variable* var = entry.first; + const ValueFlow::Value& value = entry.second; + const Token* nameTok = var->nameToken(); + const Token* initLhs = nameTok->tokAt(2); + + for (const Scope* functionScope : symboldatabase.functionScopes) { + if (functionScope->className != "main") + continue; + if (!functionScope->bodyStart || !functionScope->bodyEnd) + continue; + + valueFlowForward(const_cast(functionScope->bodyStart->next()), functionScope->bodyEnd, initLhs, value, tokenlist, errorLogger, settings); + break; + } + } + for (const Scope *functionScope : symboldatabase.functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { if (!Token::Match(tok, "[;{}] %var% =")) diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 7b36dbac035..cc049d9bdc5 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -3078,6 +3078,29 @@ class TestBufferOverrun : public TestFixture { " delete[] z;\n" "}\n"); ASSERT_EQUALS("[test.cpp:4:10]: (error) Array 'z[5]' accessed at index 7, which is out of bounds. [arrayIndexOutOfBounds]\n", errout_str()); + + // #14934 + check("int *a = new int[2];\n" + "int main() {\n" + " return a[5];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:13]: (error) Array 'a[2]' accessed at index 5, which is out of bounds. [arrayIndexOutOfBounds]\n", errout_str()); + + check("int *a = new int[2];\n" + "int main() {\n" + " a = new int[10];\n" + " return a[5];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int *a = new int[2];\n" + "void reset();\n" + "int main() {\n" + " reset();\n" + " return a[5];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } void buffer_overrun_2_struct() {