From 91d76219941f8d7c854d16d1babf413d61c67bc2 Mon Sep 17 00:00:00 2001 From: PKEuS Date: Wed, 12 Oct 2011 22:11:27 +0200 Subject: [PATCH] Fixed #3180 (New check: Detect nullpointers given to printf via variable argument list) --- lib/checknullpointer.cpp | 68 ++++++++++++++++++++++++++++++---- test/testnullpointer.cpp | 80 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 11 deletions(-) diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index 4ca5ffd50..b8ff1512b 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -22,6 +22,7 @@ #include "executionpath.h" #include "mathlib.h" #include "symboldatabase.h" +#include //--------------------------------------------------------------------------- // Register this check class (by creating a static instance of it) @@ -78,6 +79,9 @@ void CheckNullPointer::parseFunctionCall(const Token &tok, std::listvarId() > 0) + if (Token::Match(&tok, "printf|sprintf|snprintf|fprintf|fnprintf|scanf|sscanf|fscanf")) { - const std::string &formatstr(tok.tokAt(2)->str()); - const std::string::size_type pos = formatstr.find("%"); - if (pos != std::string::npos && formatstr.compare(pos,2,"%s") == 0) - var.push_back(tok.tokAt(4)); + const Token* argListTok = 0; // Points to first va_list argument + std::string formatString; + bool scan = Token::Match(&tok, "scanf|sscanf|fscanf"); + if (Token::Match(&tok, "printf|scanf ( %str% , %any%")) + { + formatString = tok.strAt(2); + argListTok = tok.tokAt(4); + } + else if (Token::Match(&tok, "sprintf|fprintf|sscanf|fscanf ( %var% , %str% , %any%")) + { + formatString = tok.strAt(4); + argListTok = tok.tokAt(6); + } + else if (Token::Match(&tok, "snprintf|fnprintf ( %var% , %any% , %str% , %any%")) + { + formatString = tok.strAt(6); + argListTok = tok.tokAt(8); + } + if (argListTok) + { + bool percent = false; + for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) + { + if (*i == '%') + { + percent = !percent; + } + else if (percent && std::isalpha(*i)) + { + if (*i == 'n' || *i == 's' || scan) + { + if ((value == 0 && argListTok->str() == "0") || (Token::Match(argListTok, "%var%") && argListTok->varId() > 0)) + { + var.push_back(argListTok); + } + } + + for (; argListTok; argListTok = argListTok->next()) // Find next argument + { + if (argListTok->str() == ",") + { + argListTok = argListTok->next(); + break; + } + } + percent = false; + } + } + } } } diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 93c000768..54b02c5aa 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -47,11 +47,13 @@ private: TEST_CASE(nullpointer9); TEST_CASE(nullpointer10); TEST_CASE(nullpointer11); // ticket #2812 - TEST_CASE(pointerCheckAndDeRef); // check if pointer is null and then dereference it - TEST_CASE(nullConstantDereference); // Dereference NULL constant - TEST_CASE(gcc_statement_expression); // Don't crash + TEST_CASE(pointerCheckAndDeRef); // check if pointer is null and then dereference it + TEST_CASE(nullConstantDereference); // Dereference NULL constant + TEST_CASE(gcc_statement_expression); // Don't crash TEST_CASE(snprintf_with_zero_size); TEST_CASE(snprintf_with_non_zero_size); + TEST_CASE(printf_with_invalid_va_argument); + TEST_CASE(scanf_with_invalid_va_argument); } void check(const char code[], bool inconclusive = false) @@ -1356,6 +1358,78 @@ private: "}"); ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); } + + void printf_with_invalid_va_argument() + { + check("void f() {\n" + " printf(\"%s\", 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); + + check("void f(char* s) {\n" + " printf(\"%s\", s);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " char* s = 0;\n" + " printf(\"%s\", s);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: s\n", errout.str()); + + + check("void f() {\n" + " printf(\"%u%s\", 0, 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); + + check("void f(char* s) {\n" + " printf(\"%u%s\", 0, s);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void f() {\n" + " char* s = 0;\n" + " printf(\"%u%s\", 123, s);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: s\n", errout.str()); + + + check("void f() {\n" + " printf(\"%%%s%%\", 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); + + check("void f(char* s) {\n" + " printf(\"text: %s, %s\", s, 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); + + + check("void f() {\n" + " char* s = \"blabla\";\n" + " printf(\"%s\", s);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + + void scanf_with_invalid_va_argument() + { + check("void f(char* s) {\n" + " sscanf(s, \"%s\", 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); + + check("void f() {\n" + " scanf(\"%d\", 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str()); + + check("void f(char* s) {\n" + " printf(\"%s\", s);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } }; REGISTER_TEST(TestNullPointer)