From 59f448da8a7f83752762c5d7390b84d249e6f0c5 Mon Sep 17 00:00:00 2001 From: Lucas Manuel Rodriguez Date: Sat, 20 Jul 2013 17:20:16 +0200 Subject: [PATCH] Fixed #4908 (False positive: void * calculation (struct member, cast)) --- lib/checksizeof.cpp | 37 +++++++++++++++++++++++++++++----- test/testsizeof.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/lib/checksizeof.cpp b/lib/checksizeof.cpp index c047ece98..8df66566b 100644 --- a/lib/checksizeof.cpp +++ b/lib/checksizeof.cpp @@ -20,6 +20,7 @@ //--------------------------------------------------------------------------- #include "checksizeof.h" #include "symboldatabase.h" +#include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) @@ -291,14 +292,40 @@ void CheckSizeof::sizeofVoid() (Token::Match(tok->tokAt(3)->variable()->typeStartToken(), "void * !!*")) && (!tok->tokAt(3)->variable()->isArray())) { // sizeof(*p) where p is of type "void*" sizeofDereferencedVoidPointerError(tok, tok->strAt(3)); - } else if (Token::Match(tok, "%var% +|-|++|--") || Token::Match(tok, "+|-|++|-- %var%")) { // Arithmetic operations on variable of type "void*" + } else if (Token::Match(tok, "%var% +|-|++|--") || + Token::Match(tok, "+|-|++|-- %var%")) { // Arithmetic operations on variable of type "void*" int index = (tok->isName()) ? 0 : 1; const Variable* var = tok->tokAt(index)->variable(); if (var && Token::Match(var->typeStartToken(), "void *")) { - if (Token::Match(tok->previous(), ") %var% +|-") && // Check for cast on operations with +|- - !Token::Match(tok->previous()->link(), "( const| void *")) - continue; - arithOperationsOnVoidPointerError(tok, tok->strAt(index), var->typeStartToken()->stringifyList(var->typeEndToken()->next())); + std::string varname = tok->str(); + // In case this 'void *' var is a member then go back to the main object + const Token* tok2 = tok->tokAt(index); + if (index == 0) { + bool isMember = false; + while (Token::Match(tok2->previous(), ".")) { + isMember = true; + if (Token::Match(tok2->tokAt(-2), ")")) + tok2 = tok2->tokAt(-2)->link(); + else if (Token::Match(tok2->tokAt(-2), "]")) + tok2 = tok2->tokAt(-2)->link()->previous(); + else + tok2 = tok2->tokAt(-2); + } + if (isMember) { + // Get 'struct.member' complete name (without spaces) + varname = tok2->stringifyList(tok->tokAt(index)->next()); + varname.erase(remove_if(varname.begin(), varname.end(), + static_cast(std::isspace)), varname.end()); + } + } + // Check for cast on operations with '+|-' + if (Token::Match(tok, "%var% +|-")) { + // Check for cast expression + if (Token::Match(tok2->previous(), ")") && !Token::Match(tok2->previous()->link(), "( const| void *")) + continue; + } + arithOperationsOnVoidPointerError(tok, varname, + var->typeStartToken()->stringifyList(var->typeEndToken()->next())); } } } diff --git a/test/testsizeof.cpp b/test/testsizeof.cpp index d2fb4bc9c..61b82ee31 100644 --- a/test/testsizeof.cpp +++ b/test/testsizeof.cpp @@ -558,6 +558,54 @@ private: " void* data2 = (void *)data + 1;\n" "}"); ASSERT_EQUALS("[test.cpp:2]: (portability) 'data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); + + // #4908 (void pointer as a member of a struct/class) + check("struct FOO {\n" + " void *data;\n" + "};\n" + "char f(struct FOO foo) {\n" + " char x = *((char*)(foo.data+1));\n" + " foo.data++;\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (portability) 'foo.data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" + "[test.cpp:6]: (portability) 'foo.data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", errout.str()); + + check("struct FOO {\n" + " void *data;\n" + "};\n" + "char f(struct FOO foo) {\n" + " char x = *((char*)foo.data+1);\n" + " return x;\n" + "}\n" + "char f2(struct FOO foo) {\n" + " char x = *((char*)((FOO)foo).data + 1);\n" + " return x;\n" + "}\n" + "char f3(struct FOO* foo) {\n" + " char x = *((char*)foo->data + 1);\n" + " return x;\n" + "}\n" + "struct BOO {\n" + " FOO data;\n" + "};\n" + "void f4(struct BOO* boo) {\n" + " char c = *((char*)boo->data.data + 1);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + check("struct FOO {\n" + " void *data;\n" + "};\n" + "char f(struct FOO* foo) {\n" + " char x = *(foo[1].data + 1);\n" + " return x;\n" + "}\n" + "void f2(struct FOO* foo) {\n" + " (foo[0]).data++;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (portability) 'foo[1].data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n" + "[test.cpp:9]: (portability) 'foo[0].data' is of type 'void *'. When using void pointers in calculations, the behaviour is undefined.\n", "", errout.str()); } };