From 0b19a52e905b752f030f36fb241102f16df22995 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:08:02 +0200 Subject: [PATCH 1/7] Update checkbufferoverrun.cpp --- lib/checkbufferoverrun.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 46d8e1c7c6c..232451ca72b 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -68,8 +68,9 @@ static const ValueFlow::Value *getBufferSizeValue(const Token *tok) auto it = std::find_if(tokenValues.cbegin(), tokenValues.cend(), std::mem_fn(&ValueFlow::Value::isBufferSizeValue)); if (it != tokenValues.cend()) return &*it; - it = std::find_if(tokenValues.cbegin(), tokenValues.cend(), std::mem_fn(&ValueFlow::Value::isContainerSizeValue)); - return it == tokenValues.cend() ? nullptr : &*it; + it = std::find_if(tokenValues.cbegin(), tokenValues.cend(), [](const ValueFlow::Value& v) { + return v.isContainerSizeValue() && v.isKnown(); + }); } static const Token* getRealBufferTok(const Token* tok) { From 88a9426aa6aef7193cffb2636b016e67fd0bfa80 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:09:41 +0200 Subject: [PATCH 2/7] Update testbufferoverrun.cpp --- test/testbufferoverrun.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 37ab7083902..1f69adb50c4 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -3564,6 +3564,14 @@ class TestBufferOverrun : public TestFixture { " memset(&a[i], 0, sizeof(a));\n" "}\n"); ASSERT_EQUALS("[test.cpp:4:16]: (error) Buffer is accessed out of bounds: &a[i] [bufferAccessOutOfBounds]\n", errout_str()); + + check("void f(const std::vector& s) {\n" // #14948 + " if (s.size() < 4)\n" + " return;\n" + " uint32_t u = 0;\n" + " std::memcpy(&u, &s[0], sizeof(u));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); } void buffer_overrun_errorpath() { From 31b88ba2d9c1ec6e0d5ee65458f24ed5fe696f69 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Wed, 29 Jul 2026 10:27:02 +0200 Subject: [PATCH 3/7] Update checkbufferoverrun.cpp --- lib/checkbufferoverrun.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 232451ca72b..87729ebbb79 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -71,6 +71,7 @@ static const ValueFlow::Value *getBufferSizeValue(const Token *tok) it = std::find_if(tokenValues.cbegin(), tokenValues.cend(), [](const ValueFlow::Value& v) { return v.isContainerSizeValue() && v.isKnown(); }); + return it == tokenValues.cend() ? nullptr : &*it; } static const Token* getRealBufferTok(const Token* tok) { From 13bc2fad6db7c7908fee347b56bfc6756c178896 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Tue, 4 Aug 2026 19:02:11 +0200 Subject: [PATCH 4/7] Allow possible values --- lib/checkbufferoverrun.cpp | 11 ++++++++--- test/testbufferoverrun.cpp | 7 +++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 87729ebbb79..8b5c68a7907 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -69,7 +69,7 @@ static const ValueFlow::Value *getBufferSizeValue(const Token *tok) if (it != tokenValues.cend()) return &*it; it = std::find_if(tokenValues.cbegin(), tokenValues.cend(), [](const ValueFlow::Value& v) { - return v.isContainerSizeValue() && v.isKnown(); + return v.isContainerSizeValue() && !v.isImpossible(); }); return it == tokenValues.cend() ? nullptr : &*it; } @@ -589,7 +589,9 @@ ValueFlow::Value CheckBufferOverrunImpl::getBufferSize(const Token *bufTok, cons if (elementSize > 0) { ValueFlow::Value bufSizeVal; bufSizeVal.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; - bufSizeVal.intvalue = value->intvalue * elementSize; + bufSizeVal.intvalue = value->intvalue * elementSize; + bufSizeVal.valueKind = value->valueKind; + bufSizeVal.errorPath = value->errorPath; return bufSizeVal; } } @@ -723,7 +725,10 @@ void CheckBufferOverrunImpl::bufferOverflow() void CheckBufferOverrunImpl::bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty) { - reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? getRealBufferTok(tok)->expressionString() : "buf"), CWE_BUFFER_OVERRUN, certainty); + const auto errorPath = getErrorPath(tok, value, "Buffer overrun"); + const auto severity = !value || value->isKnown() ? Severity::error : Severity::warning; + const std::string msg = "Buffer is accessed out of bounds: " + (tok ? getRealBufferTok(tok)->expressionString() : "buf"); + reportError(errorPath, severity, "bufferAccessOutOfBounds", msg, CWE_BUFFER_OVERRUN, certainty); } //--------------------------------------------------------------------------- diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 1f69adb50c4..fa0315f804f 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -3572,6 +3572,13 @@ class TestBufferOverrun : public TestFixture { " std::memcpy(&u, &s[0], sizeof(u));\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + check("void f(const std::vector& s) {\n" + " if (s.size() == 2) {}\n" + " uint32_t u = 0;\n" + " std::memcpy(&u, &s[0], sizeof(u));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4:21]: (warning) Buffer is accessed out of bounds: &s[0] [bufferAccessOutOfBounds]\n", errout_str()); } void buffer_overrun_errorpath() { From d9b785a04690483405ab0df4cfdbf25b3b647c25 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Tue, 4 Aug 2026 19:04:14 +0200 Subject: [PATCH 5/7] Format --- lib/checkbufferoverrun.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 8b5c68a7907..6468d766f93 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -589,7 +589,7 @@ ValueFlow::Value CheckBufferOverrunImpl::getBufferSize(const Token *bufTok, cons if (elementSize > 0) { ValueFlow::Value bufSizeVal; bufSizeVal.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; - bufSizeVal.intvalue = value->intvalue * elementSize; + bufSizeVal.intvalue = value->intvalue * elementSize; bufSizeVal.valueKind = value->valueKind; bufSizeVal.errorPath = value->errorPath; return bufSizeVal; From cc8576104bffb701e58186c149129724deddcf4b Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Wed, 5 Aug 2026 19:47:52 +0200 Subject: [PATCH 6/7] Partial fix for #14960 --- lib/checkbufferoverrun.cpp | 25 ++++++++++++++++--------- test/testbufferoverrun.cpp | 25 ++++++++++++++++++------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index 6468d766f93..e55ac8f3821 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -620,7 +620,7 @@ ValueFlow::Value CheckBufferOverrunImpl::getBufferSize(const Token *bufTok, cons } //--------------------------------------------------------------------------- -static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector &args, const MathLib::bigint bufferSize, const Settings &settings, const Tokenizer* tokenizer) +static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector &args, ValueFlow::Value& bufferSize, const Settings &settings, const Tokenizer* tokenizer) { const Token * const arg = (minsize.arg > 0 && minsize.arg - 1 < args.size()) ? args[minsize.arg - 1] : nullptr; const Token * const arg2 = (minsize.arg2 > 0 && minsize.arg2 - 1 < args.size()) ? args[minsize.arg2 - 1] : nullptr; @@ -628,11 +628,11 @@ static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::Mi switch (minsize.type) { case Library::ArgumentChecks::MinSize::Type::STRLEN: if (settings.library.isargformatstr(ftok, minsize.arg)) { - return getMinFormatStringOutputLength(args, minsize.arg, settings) < bufferSize; + return getMinFormatStringOutputLength(args, minsize.arg, settings) < bufferSize.intvalue; } else if (arg) { const Token *strtoken = arg->getValueTokenMaxStrLength(); if (strtoken) - return Token::getStrLength(strtoken) < bufferSize; + return Token::getStrLength(strtoken) < bufferSize.intvalue; } break; case Library::ArgumentChecks::MinSize::Type::ARGVALUE: { @@ -641,7 +641,14 @@ static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::Mi const int baseSize = tokenizer->sizeOfType(minsize.baseType); if (baseSize != 0) myMinsize *= baseSize; - return myMinsize <= bufferSize; + const bool ok = myMinsize <= bufferSize.intvalue; + if (!ok) { + if (bufferSize.errorPath.empty()) + bufferSize.errorPath = arg->values().front().errorPath; + if (!bufferSize.condition) + bufferSize.condition = arg->values().front().condition; + } + return ok; } break; } @@ -650,14 +657,14 @@ static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::Mi break; case Library::ArgumentChecks::MinSize::Type::MUL: if (arg && arg2 && arg->hasKnownIntValue() && arg2->hasKnownIntValue()) - return (arg->getKnownIntValue() * arg2->getKnownIntValue()) <= bufferSize; + return (arg->getKnownIntValue() * arg2->getKnownIntValue()) <= bufferSize.intvalue; break; case Library::ArgumentChecks::MinSize::Type::VALUE: { MathLib::bigint myMinsize = minsize.value; const int baseSize = tokenizer->sizeOfType(minsize.baseType); if (baseSize != 0) myMinsize *= baseSize; - return myMinsize <= bufferSize; + return myMinsize <= bufferSize.intvalue; } case Library::ArgumentChecks::MinSize::Type::NONE: break; @@ -695,7 +702,7 @@ void CheckBufferOverrunImpl::bufferOverflow() if (argtok->valueType() && argtok->valueType()->pointer == 0) continue; // TODO: strcpy(buf+10, "hello"); - const ValueFlow::Value bufferSize = getBufferSize(argtok, mSettings); + ValueFlow::Value bufferSize = getBufferSize(argtok, mSettings); if (bufferSize.intvalue <= 0) continue; // buffer size == 1 => do not warn for dynamic memory @@ -714,7 +721,7 @@ void CheckBufferOverrunImpl::bufferOverflow() } } const bool error = std::none_of(minsizes->begin(), minsizes->end(), [&](const Library::ArgumentChecks::MinSize &minsize) { - return checkBufferSize(tok, minsize, args, bufferSize.intvalue, mSettings, mTokenizer); + return checkBufferSize(tok, minsize, args, bufferSize, mSettings, mTokenizer); }); if (error) bufferOverflowError(args[argnr], &bufferSize, Certainty::normal); @@ -726,7 +733,7 @@ void CheckBufferOverrunImpl::bufferOverflow() void CheckBufferOverrunImpl::bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty) { const auto errorPath = getErrorPath(tok, value, "Buffer overrun"); - const auto severity = !value || value->isKnown() ? Severity::error : Severity::warning; + const auto severity = !value || (value->isKnown() && !value->condition) ? Severity::error : Severity::warning; const std::string msg = "Buffer is accessed out of bounds: " + (tok ? getRealBufferTok(tok)->expressionString() : "buf"); reportError(errorPath, severity, "bufferAccessOutOfBounds", msg, CWE_BUFFER_OVERRUN, certainty); } diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index fa0315f804f..3745a428650 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -3572,13 +3572,6 @@ class TestBufferOverrun : public TestFixture { " std::memcpy(&u, &s[0], sizeof(u));\n" "}\n"); ASSERT_EQUALS("", errout_str()); - - check("void f(const std::vector& s) {\n" - " if (s.size() == 2) {}\n" - " uint32_t u = 0;\n" - " std::memcpy(&u, &s[0], sizeof(u));\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:4:21]: (warning) Buffer is accessed out of bounds: &s[0] [bufferAccessOutOfBounds]\n", errout_str()); } void buffer_overrun_errorpath() { @@ -3593,6 +3586,24 @@ class TestBufferOverrun : public TestFixture { ASSERT_EQUALS("[test.cpp:3:12]: error: Buffer is accessed out of bounds: p [bufferAccessOutOfBounds]\n" "[test.cpp:2:13]: note: Assign p, buffer with size 10\n" "[test.cpp:3:12]: note: Buffer overrun\n", errout_str()); + + check("void f(const std::vector& s) {\n" + " if (s.size() == 2) {}\n" + " uint32_t u = 0;\n" + " std::memcpy(&u, &s[0], sizeof(u));\n" + "}\n", s); + ASSERT_EQUALS("[test.cpp:4:21]: warning: Buffer is accessed out of bounds: &s[0] [bufferAccessOutOfBounds]\n" + "[test.cpp:2:18]: note: Assuming that condition 's.size()==2' is not redundant\n" + "[test.cpp:4:21]: note: Buffer overrun\n", errout_str()); + + check("void f(int i) {\n" // #14960 + " int a[1];\n" + " if (i != 2) return;\n" + " memset(a, 0, i * sizeof(int));\n" + "}", s); + ASSERT_EQUALS("[test.cpp:4:12]: warning: Buffer is accessed out of bounds: a [bufferAccessOutOfBounds]\n" + "[test.cpp:3:11]: note: Assuming that condition 'i!=2' is not redundant\n" + "[test.cpp:4:12]: note: Buffer overrun\n", errout_str()); } void buffer_overrun_bailoutIfSwitch() { From 7e3ba00d1a51defe9492775d3b03d4efb86f7266 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Wed, 5 Aug 2026 20:31:50 +0200 Subject: [PATCH 7/7] Fix --- lib/checkbufferoverrun.cpp | 12 +++++++----- test/testbufferoverrun.cpp | 10 +++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index e55ac8f3821..19d2a6cc1ab 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -636,17 +636,19 @@ static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::Mi } break; case Library::ArgumentChecks::MinSize::Type::ARGVALUE: { - if (arg && arg->hasKnownIntValue()) { - MathLib::bigint myMinsize = arg->getKnownIntValue(); + if (arg) { + const ValueFlow::Value* argVal = arg->hasKnownIntValue() ? &arg->values().front() : arg->getMaxValue(true); + if (!argVal) + break; + MathLib::bigint myMinsize = argVal->intvalue; const int baseSize = tokenizer->sizeOfType(minsize.baseType); if (baseSize != 0) myMinsize *= baseSize; const bool ok = myMinsize <= bufferSize.intvalue; if (!ok) { - if (bufferSize.errorPath.empty()) - bufferSize.errorPath = arg->values().front().errorPath; + bufferSize.errorPath.insert(bufferSize.errorPath.end(), argVal->errorPath.begin(), argVal->errorPath.end()); if (!bufferSize.condition) - bufferSize.condition = arg->values().front().condition; + bufferSize.condition = argVal->condition; } return ok; } diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 3745a428650..d3db1f2ad3f 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -3600,10 +3600,18 @@ class TestBufferOverrun : public TestFixture { " int a[1];\n" " if (i != 2) return;\n" " memset(a, 0, i * sizeof(int));\n" + "}" + "void g(int i) {\n" + " int a[1];\n" + " if (i != 2) {}\n" + " memset(a, 0, i * sizeof(int));\n" "}", s); ASSERT_EQUALS("[test.cpp:4:12]: warning: Buffer is accessed out of bounds: a [bufferAccessOutOfBounds]\n" "[test.cpp:3:11]: note: Assuming that condition 'i!=2' is not redundant\n" - "[test.cpp:4:12]: note: Buffer overrun\n", errout_str()); + "[test.cpp:4:12]: note: Buffer overrun\n" + "[test.cpp:8:12]: warning: Buffer is accessed out of bounds: a [bufferAccessOutOfBounds]\n" + "[test.cpp:7:11]: note: Assuming that condition 'i!=2' is not redundant\n" + "[test.cpp:8:12]: note: Buffer overrun\n", errout_str()); } void buffer_overrun_bailoutIfSwitch() {