diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index a9c85802374..c97642b9930 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -585,6 +585,10 @@ ValueFlow::Value CheckBufferOverrunImpl::getBufferSize(const Token *bufTok, cons } bufTok = bufTok->astOperand1(); } + while (Token::Match(bufTok, ".|::")) + bufTok = bufTok->astOperand2(); + if (!bufTok) + return ValueFlow::Value(-1); } const Variable *var = bufTok->variable(); @@ -756,8 +760,16 @@ void CheckBufferOverrunImpl::bufferOverflow() if (bufferSize.intvalue <= 0) continue; // buffer size == 1 => do not warn for dynamic memory - if (bufferSize.intvalue == 1 && Token::simpleMatch(argtok->astParent(), ".")) { // TODO: check if parent was allocated dynamically - const Token *tok2 = argtok; + const Token *memberTok = argtok; + if (memberTok->isUnaryOp("&")) { + memberTok = memberTok->astOperand1(); + if (Token::simpleMatch(memberTok, "[")) + memberTok = memberTok->astOperand1(); + while (Token::Match(memberTok, ".|::")) + memberTok = memberTok->astOperand2(); + } + if (bufferSize.intvalue == 1 && memberTok && Token::simpleMatch(memberTok->astParent(), ".")) { // TODO: check if parent was allocated dynamically + const Token *tok2 = memberTok; while (Token::simpleMatch(tok2->astParent(), ".")) tok2 = tok2->astParent(); while (Token::Match(tok2, "[|.")) diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 7771d294d2c..0e0bf5ce385 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -231,6 +231,7 @@ class TestBufferOverrun : public TestFixture { TEST_CASE(buffer_overrun_37); TEST_CASE(buffer_overrun_38); TEST_CASE(buffer_overrun_39); + TEST_CASE(buffer_overrun_40); TEST_CASE(buffer_overrun_errorpath); TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch TEST_CASE(buffer_overrun_function_array_argument); @@ -3692,6 +3693,46 @@ class TestBufferOverrun : public TestFixture { errout_str()); } + void buffer_overrun_40() { + check("struct S { char a[4]; };\n" + "struct T { struct S s; };\n" + "int f(const struct T *p, const struct T *q) {\n" + " return memcmp(&p->s.a, &q->s, 16);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4:19]: (error) Buffer is accessed out of bounds: p->s.a [bufferAccessOutOfBounds]\n" + "[test.cpp:4:28]: (error) Buffer is accessed out of bounds: &q->s [bufferAccessOutOfBounds]\n", + errout_str()); + + check("struct S { char a[4]; };\n" + "struct T { struct S s; };\n" + "int f(const struct T *p, const struct T *q) {\n" + " return memcmp(&p->s.a, &q->s, 4);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { char a[4]; };\n" + "struct T { struct S s; };\n" + "void f(char *d) {\n" + " struct S s;\n" + " struct T t;\n" + " memcpy(d, &s.a, 16);\n" + " memcpy(d, &s.a[0], 16);\n" + " memcpy(d, &t.s, 16);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6:15]: (error) Buffer is accessed out of bounds: s.a [bufferAccessOutOfBounds]\n" + "[test.cpp:7:15]: (error) Buffer is accessed out of bounds: &s.a[0] [bufferAccessOutOfBounds]\n" + "[test.cpp:8:15]: (error) Buffer is accessed out of bounds: &t.s [bufferAccessOutOfBounds]\n", + errout_str()); + + check("struct Foo { int n; char data[1]; };\n" + "void f(struct Foo *p, const char *src) {\n" + " memcpy(p->data, src, 10);\n" + " memcpy(&p->data, src, 10);\n" + " memcpy(&p->data[0], src, 10);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + void buffer_overrun_errorpath() { setMultiline(); Settings s = settings0;