diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 16aba7fb0..bc03c7e03 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -661,6 +661,8 @@ static unsigned int getSizeOfType(const Token *typeTok, const Settings *settings return settings->sizeof_int; else if (typeStr == "long") return typeTok->isLong() ? settings->sizeof_long_long : settings->sizeof_long; + else if (typeStr == "wchar_t") + return settings->sizeof_wchar_t; else return 0; } @@ -692,6 +694,13 @@ static Token * valueFlowSetConstantValue(const Token *tok, const Settings *setti setTokenValue(const_cast(tok), value, settings); } else if (Token::simpleMatch(tok, "sizeof (")) { const Token *tok2 = tok->tokAt(2); + // skip over tokens to find variable or type + while (Token::Match(tok2, "%name% ::|.|[")) { + if (tok2->next()->str() == "[") + tok2 = tok2->linkAt(1)->next(); + else + tok2 = tok2->tokAt(2); + } if (tok2->enumerator() && tok2->enumerator()->scope) { long long size = settings->sizeof_int; const Token * type = tok2->enumerator()->scope->enumType; @@ -732,6 +741,35 @@ static Token * valueFlowSetConstantValue(const Token *tok, const Settings *setti value.setKnown(); setTokenValue(const_cast(tok->tokAt(4)), value, settings); } + } else if (Token::Match(tok2, "%var% )")) { + const Variable *var = tok2->variable(); + // only look for single token types (no pointers or references yet) + if (var && var->typeStartToken() == var->typeEndToken()) { + // find the size of the type + size_t size = 0; + if (var->isEnumType()) { + size = settings->sizeof_int; + if (var->type()->classScope && var->type()->classScope->enumType) + size = getSizeOfType(var->type()->classScope->enumType, settings); + } else if (!var->type()) { + size = getSizeOfType(var->typeStartToken(), settings); + } + // find the number of elements + size_t count = 1; + for (size_t i = 0; i < var->dimensions().size(); ++i) { + if (var->dimensionKnown(i)) + count *= var->dimension(i); + else + count = 0; + } + if (size && count > 0) { + ValueFlow::Value value(count * size); + if (settings->platformType != cppcheck::Platform::Unspecified) + value.setKnown(); + setTokenValue(const_cast(tok), value, settings); + setTokenValue(const_cast(tok->next()), value, settings); + } + } } else if (!tok2->type()) { const ValueType &vt = ValueType::parseDecl(tok2,settings); if (vt.pointer) { diff --git a/test/testbufferoverrun.cpp b/test/testbufferoverrun.cpp index 5fec58a88..8a224c972 100644 --- a/test/testbufferoverrun.cpp +++ b/test/testbufferoverrun.cpp @@ -150,6 +150,7 @@ private: TEST_CASE(array_index_valueflow); TEST_CASE(array_index_valueflow_pointer); TEST_CASE(array_index_function_parameter); + TEST_CASE(array_index_enum_array); // #8439 TEST_CASE(buffer_overrun_2_struct); TEST_CASE(buffer_overrun_3); @@ -171,6 +172,7 @@ private: TEST_CASE(buffer_overrun_27); // #4444 (segmentation fault) TEST_CASE(buffer_overrun_28); // Out of bound char array access TEST_CASE(buffer_overrun_29); // #7083: false positive: typedef and initialization with strings + TEST_CASE(buffer_overrun_30); // #6367 TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch TEST_CASE(buffer_overrun_function_array_argument); TEST_CASE(possible_buffer_overrun_1); // #3035 @@ -2168,6 +2170,15 @@ private: ASSERT_EQUALS("", errout.str()); } + void array_index_enum_array() { // #8439 + check("enum E : unsigned int { e1, e2 };\n" + "void f() {\n" + " E arrE[] = { e1, e2 };\n" + " arrE[sizeof(arrE)] = e1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'arrE[2]' accessed at index 8, which is out of bounds.\n", errout.str()); + } + void buffer_overrun_2_struct() { check("struct ABC\n" "{\n" @@ -2533,6 +2544,19 @@ private: } + // #6367 + void buffer_overrun_30() { + check("struct S { int m[9]; };\n" + "int f(S * s) {\n" + " return s->m[sizeof(s->m)];\n" + "}\n" + ); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 's->m[9]' accessed at index 36, which is out of bounds.\n" + "[test.cpp:3]: (error) Array 's.m[9]' accessed at index 36, which is out of bounds.\n", + errout.str()); + } + + void buffer_overrun_bailoutIfSwitch() { // No false positive check("void f1(char *s) {\n" diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 8a0805ea4..d8b97406c 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -59,6 +59,7 @@ private: TEST_CASE(valueFlowBitAnd); TEST_CASE(valueFlowCalculations); + TEST_CASE(valueFlowSizeof); TEST_CASE(valueFlowErrorPath); @@ -559,22 +560,6 @@ private: ASSERT_EQUALS(1U, values.size()); ASSERT_EQUALS(-10, values.back().intvalue); - // sizeof - code = "void f() {\n" - " x = sizeof(int);\n" - "}"; - values = tokenValues(code,"( int )"); - ASSERT_EQUALS(1U, values.size()); - ASSERT_EQUALS(settings.sizeof_int, values.back().intvalue); - - code = "void f() {\n" - " struct S *a[10];" - " x = sizeof(a) / sizeof(a[0]);\n" - "}"; - values = tokenValues(code,"/"); - ASSERT_EQUALS(1U, values.size()); - ASSERT_EQUALS(10, values.back().intvalue); - // function call => calculation code = "void f(int x) {\n" " a = x + 8;\n" @@ -621,6 +606,175 @@ private: ASSERT_EQUALS(1, values.front().intvalue); } + void valueFlowSizeof() { + const char *code; + std::list values; + +#define CHECK(A, B) \ + code = "void f() {\n" \ + " x = sizeof(" A ");\n" \ + "}"; \ + values = tokenValues(code,"( " A " )"); \ + ASSERT_EQUALS(1U, values.size()); \ + ASSERT_EQUALS(B, values.back().intvalue); + + // standard types + CHECK("void *", settings.sizeof_pointer); + CHECK("char", 1U); + CHECK("short", settings.sizeof_short); + CHECK("int", settings.sizeof_int); + CHECK("long", settings.sizeof_long); +#undef CHECK + + // array size + code = "void f() {\n" + " struct S *a[10];" + " x = sizeof(a) / sizeof(a[0]);\n" + "}"; + values = tokenValues(code,"/"); + ASSERT_EQUALS(1U, values.size()); + ASSERT_EQUALS(10, values.back().intvalue); + +#define CHECK(A, B, C, D) \ + code = "enum " A " E " B " { E0, E1 };\n" \ + "void f() {\n" \ + " x = sizeof(" C ");\n" \ + "}"; \ + values = tokenValues(code,"( " C " )"); \ + ASSERT_EQUALS(1U, values.size()); \ + ASSERT_EQUALS(D, values.back().intvalue); + + // enums + CHECK("", "", "E", settings.sizeof_int); + + // typed enums + CHECK("", ": char", "E", 1U); + CHECK("", ": signed char", "E", 1U); + CHECK("", ": unsigned char", "E", 1U); + CHECK("", ": short", "E", settings.sizeof_short); + CHECK("", ": signed short", "E", settings.sizeof_short); + CHECK("", ": unsigned short", "E", settings.sizeof_short); + CHECK("", ": int", "E", settings.sizeof_int); + CHECK("", ": signed int", "E", settings.sizeof_int); + CHECK("", ": unsigned int", "E", settings.sizeof_int); + CHECK("", ": long", "E", settings.sizeof_long); + CHECK("", ": signed long", "E", settings.sizeof_long); + CHECK("", ": unsigned long", "E", settings.sizeof_long); + CHECK("", ": long long", "E", settings.sizeof_long_long); + CHECK("", ": signed long long", "E", settings.sizeof_long_long); + CHECK("", ": unsigned long long", "E", settings.sizeof_long_long); + CHECK("", ": wchar_t", "E", settings.sizeof_wchar_t); + CHECK("", ": size_t", "E", settings.sizeof_size_t); + + // enumerators + CHECK("", "", "E0", settings.sizeof_int); + + // typed enumerators + CHECK("", ": char", "E0", 1U); + CHECK("", ": signed char", "E0", 1U); + CHECK("", ": unsigned char", "E0", 1U); + CHECK("", ": short", "E0", settings.sizeof_short); + CHECK("", ": signed short", "E0", settings.sizeof_short); + CHECK("", ": unsigned short", "E0", settings.sizeof_short); + CHECK("", ": int", "E0", settings.sizeof_int); + CHECK("", ": signed int", "E0", settings.sizeof_int); + CHECK("", ": unsigned int", "E0", settings.sizeof_int); + CHECK("", ": long", "E0", settings.sizeof_long); + CHECK("", ": signed long", "E0", settings.sizeof_long); + CHECK("", ": unsigned long", "E0", settings.sizeof_long); + CHECK("", ": long long", "E0", settings.sizeof_long_long); + CHECK("", ": signed long long", "E0", settings.sizeof_long_long); + CHECK("", ": unsigned long long", "E0", settings.sizeof_long_long); + CHECK("", ": wchar_t", "E0", settings.sizeof_wchar_t); + CHECK("", ": size_t", "E0", settings.sizeof_size_t); + + // class typed enumerators + CHECK("class", ": char", "E :: E0", 1U); + CHECK("class", ": signed char", "E :: E0", 1U); + CHECK("class", ": unsigned char", "E :: E0", 1U); + CHECK("class", ": short", "E :: E0", settings.sizeof_short); + CHECK("class", ": signed short", "E :: E0", settings.sizeof_short); + CHECK("class", ": unsigned short", "E :: E0", settings.sizeof_short); + CHECK("class", ": int", "E :: E0", settings.sizeof_int); + CHECK("class", ": signed int", "E :: E0", settings.sizeof_int); + CHECK("class", ": unsigned int", "E :: E0", settings.sizeof_int); + CHECK("class", ": long", "E :: E0", settings.sizeof_long); + CHECK("class", ": signed long", "E :: E0", settings.sizeof_long); + CHECK("class", ": unsigned long", "E :: E0", settings.sizeof_long); + CHECK("class", ": long long", "E :: E0", settings.sizeof_long_long); + CHECK("class", ": signed long long", "E :: E0", settings.sizeof_long_long); + CHECK("class", ": unsigned long long", "E :: E0", settings.sizeof_long_long); + CHECK("class", ": wchar_t", "E :: E0", settings.sizeof_wchar_t); + CHECK("class", ": size_t", "E :: E0", settings.sizeof_size_t); +#undef CHECK + +#define CHECK(A, B) \ + code = "enum E " A " { E0, E1 };\n" \ + "void f() {\n" \ + " E arrE[] = { E0, E1 };\n" \ + " x = sizeof(arrE);\n" \ + "}"; \ + values = tokenValues(code,"( arrE )"); \ + ASSERT_EQUALS(1U, values.size()); \ + ASSERT_EQUALS(B * 2U, values.back().intvalue); + + // enum array + CHECK("", settings.sizeof_int); + + // typed enum array + CHECK(": char", 1U); + CHECK(": signed char", 1U); + CHECK(": unsigned char", 1U); + CHECK(": short", settings.sizeof_short); + CHECK(": signed short", settings.sizeof_short); + CHECK(": unsigned short", settings.sizeof_short); + CHECK(": int", settings.sizeof_int); + CHECK(": signed int", settings.sizeof_int); + CHECK(": unsigned int", settings.sizeof_int); + CHECK(": long", settings.sizeof_long); + CHECK(": signed long", settings.sizeof_long); + CHECK(": unsigned long", settings.sizeof_long); + CHECK(": long long", settings.sizeof_long_long); + CHECK(": signed long long", settings.sizeof_long_long); + CHECK(": unsigned long long", settings.sizeof_long_long); + CHECK(": wchar_t", settings.sizeof_wchar_t); + CHECK(": size_t", settings.sizeof_size_t); +#undef CHECK + +#define CHECK(A, B) \ + code = "enum class E " A " { E0, E1 };\n" \ + "void f() {\n" \ + " E arrE[] = { E::E0, E::E1 };\n" \ + " x = sizeof(arrE);\n" \ + "}"; \ + values = tokenValues(code,"( arrE )"); \ + ASSERT_EQUALS(1U, values.size()); \ + ASSERT_EQUALS(B * 2U, values.back().intvalue); + + // enum array + CHECK("", settings.sizeof_int); + + // typed enum array + CHECK(": char", 1U); + CHECK(": signed char", 1U); + CHECK(": unsigned char", 1U); + CHECK(": short", settings.sizeof_short); + CHECK(": signed short", settings.sizeof_short); + CHECK(": unsigned short", settings.sizeof_short); + CHECK(": int", settings.sizeof_int); + CHECK(": signed int", settings.sizeof_int); + CHECK(": unsigned int", settings.sizeof_int); + CHECK(": long", settings.sizeof_long); + CHECK(": signed long", settings.sizeof_long); + CHECK(": unsigned long", settings.sizeof_long); + CHECK(": long long", settings.sizeof_long_long); + CHECK(": signed long long", settings.sizeof_long_long); + CHECK(": unsigned long long", settings.sizeof_long_long); + CHECK(": wchar_t", settings.sizeof_wchar_t); + CHECK(": size_t", settings.sizeof_size_t); +#undef CHECK + } + void valueFlowErrorPath() { const char *code;