Skip to content

Commit ec232da

Browse files
authored
Fix #7570: Support address-of operator on variables in getBufferSize() (#7767)
Detect address-of token in `getBufferSize()` and get the underlying variable's corresponding buffer size. `stringNotZeroTerminated()` also calls `getBuffersize()`, so it will also benefit.
1 parent e5f0b8e commit ec232da

File tree

2 files changed

+62
-4
lines changed

2 files changed

+62
-4
lines changed

lib/checkbufferoverrun.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ static const ValueFlow::Value *getBufferSizeValue(const Token *tok)
7575
return it == tokenValues.cend() ? nullptr : &*it;
7676
}
7777

78+
static const Token* getRealBufferTok(const Token* tok) {
79+
if (!tok->isUnaryOp("&"))
80+
return tok;
81+
82+
const Token* op = tok->astOperand1();
83+
return (op->valueType() && op->valueType()->pointer) ? op : tok;
84+
}
85+
7886
static int getMinFormatStringOutputLength(const std::vector<const Token*> &parameters, nonneg int formatStringArgNr)
7987
{
8088
if (formatStringArgNr <= 0 || formatStringArgNr > parameters.size())
@@ -553,6 +561,8 @@ ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const
553561
{
554562
if (!bufTok->valueType())
555563
return ValueFlow::Value(-1);
564+
if (bufTok->isUnaryOp("&"))
565+
bufTok = bufTok->astOperand1();
556566
const Variable *var = bufTok->variable();
557567

558568
if (!var || var->dimensions().empty()) {
@@ -653,7 +663,7 @@ void CheckBufferOverrun::bufferOverflow()
653663
argtok = argtok->astOperand2() ? argtok->astOperand2() : argtok->astOperand1();
654664
while (Token::Match(argtok, ".|::"))
655665
argtok = argtok->astOperand2();
656-
if (!argtok || !argtok->variable())
666+
if (!argtok)
657667
continue;
658668
if (argtok->valueType() && argtok->valueType()->pointer == 0)
659669
continue;
@@ -688,7 +698,7 @@ void CheckBufferOverrun::bufferOverflow()
688698

689699
void CheckBufferOverrun::bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty)
690700
{
691-
reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? tok->expressionString() : "buf"), CWE_BUFFER_OVERRUN, certainty);
701+
reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? getRealBufferTok(tok)->expressionString() : "buf"), CWE_BUFFER_OVERRUN, certainty);
692702
}
693703

694704
//---------------------------------------------------------------------------
@@ -798,7 +808,7 @@ void CheckBufferOverrun::stringNotZeroTerminated()
798808
if (isZeroTerminated)
799809
continue;
800810
// TODO: Locate unsafe string usage..
801-
terminateStrncpyError(tok, args[0]->expressionString());
811+
terminateStrncpyError(tok, getRealBufferTok(args[0])->expressionString());
802812
}
803813
}
804814
}

test/testbufferoverrun.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ class TestBufferOverrun : public TestFixture {
232232
TEST_CASE(buffer_overrun_function_array_argument);
233233
TEST_CASE(possible_buffer_overrun_1); // #3035
234234
TEST_CASE(buffer_overrun_readSizeFromCfg);
235+
TEST_CASE(buffer_overrun_handle_addr_of_var); // ticket #7570 - correctly handle &var
235236

236237
TEST_CASE(valueflow_string); // using ValueFlow string values in checking
237238

@@ -271,6 +272,7 @@ class TestBufferOverrun : public TestFixture {
271272
TEST_CASE(terminateStrncpy3);
272273
TEST_CASE(terminateStrncpy4);
273274
TEST_CASE(terminateStrncpy5); // #9944
275+
TEST_CASE(terminateStrncpy_handle_addr_of_var); // #7570
274276
TEST_CASE(recursive_long_time);
275277

276278
TEST_CASE(crash1); // Ticket #1587 - crash
@@ -3724,6 +3726,38 @@ class TestBufferOverrun : public TestFixture {
37243726
ASSERT_EQUALS("[test.cpp:3:16]: (error) Buffer is accessed out of bounds: ms.str [bufferAccessOutOfBounds]\n", errout_str());
37253727
}
37263728

3729+
void buffer_overrun_handle_addr_of_var() { // #7570
3730+
check("void f() {\n"
3731+
" int i;\n"
3732+
" memset(i, 0, 1000);\n"
3733+
"}");
3734+
ASSERT_EQUALS("", errout_str());
3735+
3736+
check("void f() {\n"
3737+
" int i;\n"
3738+
" memset(&i, 0, 1000);\n"
3739+
"}");
3740+
ASSERT_EQUALS("[test.cpp:3:10]: (error) Buffer is accessed out of bounds: &i [bufferAccessOutOfBounds]\n", errout_str());
3741+
3742+
check("void f() {\n"
3743+
" int i[2];\n"
3744+
" memset(&i, 0, 1000);\n"
3745+
"}");
3746+
ASSERT_EQUALS("[test.cpp:3:10]: (error) Buffer is accessed out of bounds: i [bufferAccessOutOfBounds]\n", errout_str());
3747+
3748+
check("void f() {\n"
3749+
" int i;\n"
3750+
" memset(&i, 0, sizeof(i));\n"
3751+
"}");
3752+
ASSERT_EQUALS("", errout_str());
3753+
3754+
check("void f() {\n"
3755+
" int i[10];\n"
3756+
" memset(&i[1], 0, 1000);\n"
3757+
"}");
3758+
TODO_ASSERT_EQUALS("[test.cpp:3:10]: (error) Buffer is accessed out of bounds: &i[1] [bufferAccessOutOfBounds]\n", "", errout_str());
3759+
}
3760+
37273761
void valueflow_string() { // using ValueFlow string values in checking
37283762
check("char f() {\n"
37293763
" const char *x = s;\n"
@@ -4321,7 +4355,7 @@ class TestBufferOverrun : public TestFixture {
43214355
" char c;\n"
43224356
" mymemset(&c, 0, 4);\n"
43234357
"}", dinit(CheckOptions, $.s = &settings));
4324-
TODO_ASSERT_EQUALS("[test.cpp:3:14]: (error) Buffer is accessed out of bounds: c [bufferAccessOutOfBounds]\n", "", errout_str());
4358+
ASSERT_EQUALS("[test.cpp:3:12]: (error) Buffer is accessed out of bounds: &c [bufferAccessOutOfBounds]\n", errout_str());
43254359

43264360
// ticket #2121 - buffer access out of bounds when using uint32_t
43274361
check("void f(void) {\n"
@@ -4685,6 +4719,20 @@ class TestBufferOverrun : public TestFixture {
46854719
}
46864720
// extracttests.enable
46874721

4722+
void terminateStrncpy_handle_addr_of_var() { // #7570
4723+
check("void foo() {\n"
4724+
" char c[6];\n"
4725+
" strncpy(&c, \"hello!\", 6);\n"
4726+
"}");
4727+
ASSERT_EQUALS("[test.cpp:3:3]: (warning, inconclusive) The buffer 'c' may not be null-terminated after the call to strncpy(). [terminateStrncpy]\n", errout_str());
4728+
4729+
check("void foo() {\n"
4730+
" char c[6];\n"
4731+
" strncpy(&c, \"hello\\0\", 6);\n"
4732+
"}");
4733+
ASSERT_EQUALS("", errout_str());
4734+
}
4735+
46884736
void recursive_long_time() {
46894737
// Just test that recursive check doesn't take long time
46904738
check("char *f2 ( char *b )\n"

0 commit comments

Comments
 (0)