Moved several hardcoded function names in format string checking into libraries (std.cfg and windows.cfg).

Added support for loading a library in test suite.
This commit is contained in:
PKEuS 2014-01-12 03:44:24 -08:00
parent 21d317b7d6
commit b775714e3d
8 changed files with 117 additions and 51 deletions

View File

@ -55,5 +55,20 @@
<function name="wcstoll"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function> <function name="wcstoll"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="wcstoul"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function> <function name="wcstoul"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="wcstoull"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function> <function name="wcstoull"> <leak-ignore/> <arg nr="3"><valid>0,2-36</valid></arg> </function>
<function name="printf"> <noreturn>false</noreturn> <formatstr/> <arg nr="1"><formatstr/></arg> </function>
<function name="wprintf"> <noreturn>false</noreturn> <formatstr/> <arg nr="1"><formatstr/></arg> </function>
<function name="sprintf"> <noreturn>false</noreturn> <formatstr/> <arg nr="2"><formatstr/></arg> </function>
<function name="fprintf"> <noreturn>false</noreturn> <formatstr/> <arg nr="2"><formatstr/></arg> </function>
<function name="fwprintf"> <noreturn>false</noreturn> <formatstr/> <arg nr="2"><formatstr/></arg> </function>
<function name="snprintf"> <noreturn>false</noreturn> <formatstr/> <arg nr="3"><formatstr/></arg> </function>
<function name="fnprintf"> <noreturn>false</noreturn> <formatstr/> <arg nr="3"><formatstr/></arg> </function>
<function name="scanf"> <noreturn>false</noreturn> <formatstr scan="true"/> <arg nr="1"><formatstr/></arg> </function>
<function name="wscanf"> <noreturn>false</noreturn> <formatstr scan="true"/> <arg nr="1"><formatstr/></arg> </function>
<function name="sscanf"> <noreturn>false</noreturn> <formatstr scan="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="fscanf"> <noreturn>false</noreturn> <formatstr scan="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="fwscanf"> <noreturn>false</noreturn> <formatstr scan="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="swscanf"> <noreturn>false</noreturn> <formatstr scan="true"/> <arg nr="2"><formatstr/></arg> </function>
</def> </def>

View File

@ -9,4 +9,18 @@
<noreturn>false</noreturn> <noreturn>false</noreturn>
</function> </function>
<function name="printf_s"> <noreturn>false</noreturn> <formatstr secure="true"/> <arg nr="1"><formatstr/></arg> </function>
<function name="wprintf_s"> <noreturn>false</noreturn> <formatstr secure="true"/> <arg nr="1"><formatstr/></arg> </function>
<function name="fprintf_s"> <noreturn>false</noreturn> <formatstr secure="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="fwprintf_s"> <noreturn>false</noreturn> <formatstr secure="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="_snprintf"> <noreturn>false</noreturn> <formatstr secure="true"/> <arg nr="3"><formatstr/></arg> </function>
<function name="_snwprintf"> <noreturn>false</noreturn> <formatstr secure="true"/> <arg nr="3"><formatstr/></arg> </function>
<function name="scanf_s"> <noreturn>false</noreturn> <formatstr scan="true" secure="true"/> <arg nr="1"><formatstr/></arg> </function>
<function name="wscanf_s"> <noreturn>false</noreturn> <formatstr scan="true" secure="true"/> <arg nr="1"><formatstr/></arg> </function>
<function name="sscanf_s"> <noreturn>false</noreturn> <formatstr scan="true" secure="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="fscanf_s"> <noreturn>false</noreturn> <formatstr scan="true" secure="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="fwscanf_s"> <noreturn>false</noreturn> <formatstr scan="true" secure="true"/> <arg nr="2"><formatstr/></arg> </function>
<function name="swscanf_s"> <noreturn>false</noreturn> <formatstr scan="true" secure="true"/> <arg nr="2"><formatstr/></arg> </function>
</def> </def>

View File

@ -457,43 +457,38 @@ void CheckIO::checkWrongPrintfScanfArguments()
const Token* formatStringTok = 0; // Points to format string token const Token* formatStringTok = 0; // Points to format string token
std::string formatString; std::string formatString;
if (Token::Match(tok->next(), "( %any%")) { bool scan = false;
const Token *arg = tok->tokAt(2); bool scanf_s = false;
int argnr = 1; int formatStringArgNo = -1;
while (arg) {
if (Token::Match(arg, "%str% [,)]") && _settings->library.isargformatstr(tok->str(),argnr)) { if (Token::Match(tok->next(), "( %any%") && _settings->library.formatstr_function(tok->str())) {
formatStringTok = arg; const std::map<int, Library::ArgumentChecks>& argumentChecks = _settings->library.argumentChecks.at(tok->str());
if (arg->strAt(1) == ",") for (std::map<int, Library::ArgumentChecks>::const_iterator i = argumentChecks.cbegin(); i != argumentChecks.cend(); ++i) {
argListTok = arg->tokAt(2); if (i->second.formatstr) {
else formatStringArgNo = i->first - 1;
argListTok = 0;
break; break;
} }
arg = arg->nextArgument();
argnr++;
}
} }
if (formatStringTok) { scan = _settings->library.formatstr_scan(tok->str());
/* formatstring found in library */ scanf_s = _settings->library.formatstr_secure(tok->str());
} else if (Token::Match(tok, "printf|scanf|wprintf|wscanf (") || }
(windows && (Token::Match(tok, "printf_s|wprintf_s|scanf_s|wscanf_s (") ||
(Token::Match(tok, "Format|AppendFormat (") && if (formatStringArgNo >= 0) {
// formatstring found in library. Find format string and first argument belonging to format string.
if (!findFormat(formatStringArgNo, tok->tokAt(2), &formatStringTok, &argListTok))
continue;
} else if (windows && Token::Match(tok, "Format|AppendFormat (") &&
Token::Match(tok->tokAt(-2), "%var% .") && tok->tokAt(-2)->variable() && Token::Match(tok->tokAt(-2), "%var% .") && tok->tokAt(-2)->variable() &&
tok->tokAt(-2)->variable()->typeStartToken()->str() == "CString")))) { tok->tokAt(-2)->variable()->typeStartToken()->str() == "CString") {
// Find second parameter and format string // Find second parameter and format string
if (!findFormat(0, tok->tokAt(2), &formatStringTok, &argListTok)) if (!findFormat(0, tok->tokAt(2), &formatStringTok, &argListTok))
continue; continue;
} else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf|swscanf|fwprintf|fwscanf ( %any%") || } else if (Token::simpleMatch(tok, "swprintf (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) {
(Token::simpleMatch(tok, "swprintf (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) ||
(windows && Token::Match(tok, "sscanf_s|swscanf_s|fscanf_s|fwscanf_s|fprintf_s|fwprintf_s ( %any%"))) {
// Find third parameter and format string // Find third parameter and format string
if (!findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok)) if (!findFormat(1, tok->tokAt(2), &formatStringTok, &argListTok))
continue; continue;
} else if (Token::Match(tok, "snprintf|fnprintf (") || } else if (Token::simpleMatch(tok, "swprintf (") && !Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) {
(windows && Token::Match(tok, "_snprintf|_snwprintf (")) ||
(Token::simpleMatch(tok, "swprintf (") && !Token::Match(tok->tokAt(2)->nextArgument(), "%str%"))) {
// Find forth parameter and format string // Find forth parameter and format string
if (!findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok)) if (!findFormat(2, tok->tokAt(2), &formatStringTok, &argListTok))
continue; continue;
@ -529,8 +524,6 @@ void CheckIO::checkWrongPrintfScanfArguments()
continue; continue;
// Count format string parameters.. // Count format string parameters..
bool scanf_s = windows ? Token::Match(tok, "scanf_s|wscanf_s|sscanf_s|swscanf_s|fscanf_s|fwscanf_s") : false;
bool scan = Token::Match(tok, "sscanf|fscanf|scanf|swscanf|fwscanf|wscanf") || scanf_s;
unsigned int numFormat = 0; unsigned int numFormat = 0;
unsigned int numSecure = 0; unsigned int numSecure = 0;
bool percent = false; bool percent = false;

View File

@ -186,6 +186,10 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
argumentChecks[name][nr].valid = valid; argumentChecks[name][nr].valid = valid;
} else if (strcmp(functionnode->Name(), "ignorefunction") == 0) { } else if (strcmp(functionnode->Name(), "ignorefunction") == 0) {
_ignorefunction.insert(name); _ignorefunction.insert(name);
} else if (strcmp(functionnode->Name(), "formatstr") == 0) {
const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan");
const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure");
_formatstr[name] = std::make_pair(scan && scan->BoolValue(), secure && secure->BoolValue());
} else } else
return false; return false;
} }

View File

@ -84,6 +84,18 @@ public:
return ((id > 0) && ((id & 1) == 1)); return ((id > 0) && ((id & 1) == 1));
} }
bool formatstr_function(const std::string& funcname) const {
return _formatstr.find(funcname) != _formatstr.cend();
}
bool formatstr_scan(const std::string& funcname) const {
return _formatstr.at(funcname).first;
}
bool formatstr_secure(const std::string& funcname) const {
return _formatstr.at(funcname).second;
}
std::set<std::string> use; std::set<std::string> use;
std::set<std::string> leakignore; std::set<std::string> leakignore;
@ -326,6 +338,7 @@ private:
std::map<std::string, ExportedFunctions> _exporters; // keywords that export variables/functions to libraries (meta-code/macros) std::map<std::string, ExportedFunctions> _exporters; // keywords that export variables/functions to libraries (meta-code/macros)
std::map<std::string, std::set<std::string> > _importers; // keywords that import variables/functions std::map<std::string, std::set<std::string> > _importers; // keywords that import variables/functions
std::map<std::string, std::map<std::string,int> > _reflection; // invocation of reflection std::map<std::string, std::map<std::string,int> > _reflection; // invocation of reflection
std::map<std::string, std::pair<bool, bool> > _formatstr; // Parameters for format string checking
const ArgumentChecks * getarg(const std::string &functionName, int argnr) const { const ArgumentChecks * getarg(const std::string &functionName, int argnr) const {

View File

@ -53,11 +53,9 @@ private:
TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920 TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920
TEST_CASE(testMicrosoftSecurePrintfArgument); TEST_CASE(testMicrosoftSecurePrintfArgument);
TEST_CASE(testMicrosoftSecureScanfArgument); TEST_CASE(testMicrosoftSecureScanfArgument);
TEST_CASE(testlibrarycfg); // library configuration
} }
void check(const char code[], bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified, Library *lib = NULL) { void check(const char code[], bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified) {
// Clear the error buffer.. // Clear the error buffer..
errout.str(""); errout.str("");
@ -69,8 +67,7 @@ private:
settings.inconclusive = inconclusive; settings.inconclusive = inconclusive;
settings.platform(platform); settings.platform(platform);
if (lib) settings.library = _lib;
settings.library = *lib;
// Tokenize.. // Tokenize..
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
@ -437,6 +434,8 @@ private:
void testScanf1() { void testScanf1() {
LOAD_LIB("std.cfg");
check("void foo() {\n" check("void foo() {\n"
" int a, b;\n" " int a, b;\n"
" FILE *file = fopen(\"test\", \"r\");\n" " FILE *file = fopen(\"test\", \"r\");\n"
@ -455,6 +454,8 @@ private:
} }
void testScanf2() { void testScanf2() {
LOAD_LIB("std.cfg");
check("void foo() {\n" check("void foo() {\n"
" scanf(\"%5s\", bar);\n" // Width specifier given " scanf(\"%5s\", bar);\n" // Width specifier given
" scanf(\"%5[^~]\", bar);\n" // Width specifier given " scanf(\"%5[^~]\", bar);\n" // Width specifier given
@ -481,6 +482,7 @@ private:
} }
void testScanf4() { // ticket #2553 void testScanf4() { // ticket #2553
LOAD_LIB("std.cfg");
check("void f()\n" check("void f()\n"
"{\n" "{\n"
@ -494,6 +496,8 @@ private:
void testScanfArgument() { void testScanfArgument() {
LOAD_LIB("std.cfg");
check("void foo() {\n" check("void foo() {\n"
" scanf(\"%1d\", &foo);\n" " scanf(\"%1d\", &foo);\n"
" sscanf(bar, \"%1d\", &foo);\n" " sscanf(bar, \"%1d\", &foo);\n"
@ -1322,6 +1326,7 @@ private:
} }
void testPrintfArgument() { void testPrintfArgument() {
LOAD_LIB("std.cfg");
check("void foo() {\n" check("void foo() {\n"
" printf(\"%u\");\n" " printf(\"%u\");\n"
" printf(\"%u%s\", 123);\n" " printf(\"%u%s\", 123);\n"
@ -2107,6 +2112,8 @@ private:
} }
void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings
LOAD_LIB("std.cfg");
check("void foo() {" check("void foo() {"
" int bar;" " int bar;"
" printf(\"%1$d\", 1);" " printf(\"%1$d\", 1);"
@ -2131,6 +2138,9 @@ private:
void testMicrosoftPrintfArgument() { void testMicrosoftPrintfArgument() {
LOAD_LIB("std.cfg");
LOAD_LIB("windows.cfg");
check("void foo() {\n" check("void foo() {\n"
" size_t s;\n" " size_t s;\n"
" ptrdiff_t p;\n" " ptrdiff_t p;\n"
@ -2220,6 +2230,9 @@ private:
} }
void testMicrosoftScanfArgument() { void testMicrosoftScanfArgument() {
LOAD_LIB("std.cfg");
LOAD_LIB("windows.cfg");
check("void foo() {\n" check("void foo() {\n"
" size_t s;\n" " size_t s;\n"
" ptrdiff_t p;\n" " ptrdiff_t p;\n"
@ -2324,6 +2337,9 @@ private:
} }
void testMicrosoftSecurePrintfArgument() { void testMicrosoftSecurePrintfArgument() {
LOAD_LIB("std.cfg");
LOAD_LIB("windows.cfg");
check("void foo() {\n" check("void foo() {\n"
" int i;\n" " int i;\n"
" unsigned int u;\n" " unsigned int u;\n"
@ -2514,6 +2530,8 @@ private:
} }
void testMicrosoftSecureScanfArgument() { void testMicrosoftSecureScanfArgument() {
LOAD_LIB("windows.cfg");
check("void foo() {\n" check("void foo() {\n"
" int i;\n" " int i;\n"
" unsigned int u;\n" " unsigned int u;\n"
@ -2644,22 +2662,6 @@ private:
"}\n", false, false, Settings::Win32W); "}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void testlibrarycfg() {
const char code[] = "void f() {\n"
" format(\"%s\");\n"
"}";
// no error if configuration for 'format' is not provided
check(code);
ASSERT_EQUALS("", errout.str());
// error if configuration for 'format' is provided
Library lib;
lib.argumentChecks["format"][1].formatstr = true;
check(code, false, false, Settings::Unspecified, &lib);
ASSERT_EQUALS("[test.cpp:2]: (error) format format string requires 1 parameter but only 0 are given.\n", errout.str());
}
}; };
REGISTER_TEST(TestIO) REGISTER_TEST(TestIO)

View File

@ -62,6 +62,7 @@ unsigned int TestFixture::countTests;
std::size_t TestFixture::fails_counter = 0; std::size_t TestFixture::fails_counter = 0;
std::size_t TestFixture::todos_counter = 0; std::size_t TestFixture::todos_counter = 0;
std::size_t TestFixture::succeeded_todos_counter = 0; std::size_t TestFixture::succeeded_todos_counter = 0;
std::set<std::string> TestFixture::missingLibs;
TestFixture::TestFixture(const std::string &_name) TestFixture::TestFixture(const std::string &_name)
:classname(_name) :classname(_name)
@ -206,6 +207,11 @@ void TestFixture::assertThrowFail(const char *filename, unsigned int linenr) con
} }
} }
void TestFixture::complainMissingLib(const char* libname) const
{
missingLibs.insert(libname);
}
void TestFixture::run(const std::string &str) void TestFixture::run(const std::string &str)
{ {
testToRun = str; testToRun = str;
@ -261,6 +267,13 @@ std::size_t TestFixture::runTests(const options& args)
std::cerr << "Tests failed: " << fails_counter << std::endl << std::endl; std::cerr << "Tests failed: " << fails_counter << std::endl << std::endl;
std::cerr << errmsg.str(); std::cerr << errmsg.str();
if (!missingLibs.empty()) {
std::cerr << "Missing libraries: ";
for (std::set<std::string>::const_iterator i = missingLibs.cbegin(); i != missingLibs.cend(); ++i)
std::cerr << *i << " ";
std::cerr << std::endl << std::endl;
}
std::cerr.flush(); std::cerr.flush();
return fails_counter; return fails_counter;
} }

View File

@ -21,8 +21,10 @@
#define testsuiteH #define testsuiteH
#include <sstream> #include <sstream>
#include <set>
#include "errorlogger.h" #include "errorlogger.h"
#include "redirect.h" #include "redirect.h"
#include "library.h"
class options; class options;
@ -33,8 +35,10 @@ private:
static std::size_t fails_counter; static std::size_t fails_counter;
static std::size_t todos_counter; static std::size_t todos_counter;
static std::size_t succeeded_todos_counter; static std::size_t succeeded_todos_counter;
static std::set<std::string> missingLibs;
protected: protected:
Library _lib;
std::string classname; std::string classname;
std::string testToRun; std::string testToRun;
bool gcc_style_errors; bool gcc_style_errors;
@ -57,6 +61,7 @@ protected:
void todoAssertEquals(const char *filename, unsigned int linenr, long long wanted, void todoAssertEquals(const char *filename, unsigned int linenr, long long wanted,
long long current, long long actual) const; long long current, long long actual) const;
void assertThrowFail(const char *filename, unsigned int linenr) const; void assertThrowFail(const char *filename, unsigned int linenr) const;
void complainMissingLib(const char* libname) const;
void processOptions(const options& args); void processOptions(const options& args);
public: public:
virtual void reportOut(const std::string &outmsg); virtual void reportOut(const std::string &outmsg);
@ -70,7 +75,7 @@ public:
static std::size_t runTests(const options& args); static std::size_t runTests(const options& args);
}; };
#define TEST_CASE( NAME ) if ( runTest(#NAME) ) { currentTest = classname + "::" + #NAME; if (quiet_tests) { REDIRECT; NAME(); } else { NAME ();} } #define TEST_CASE( NAME ) if ( runTest(#NAME) ) { _lib = Library(); currentTest = classname + "::" + #NAME; if (quiet_tests) { REDIRECT; NAME(); } else { NAME ();} }
#define ASSERT( CONDITION ) assert_(__FILE__, __LINE__, CONDITION) #define ASSERT( CONDITION ) assert_(__FILE__, __LINE__, CONDITION)
#define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) #define ASSERT_EQUALS( EXPECTED , ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL)
#define ASSERT_EQUALS_DOUBLE( EXPECTED , ACTUAL ) assertEqualsDouble(__FILE__, __LINE__, EXPECTED, ACTUAL) #define ASSERT_EQUALS_DOUBLE( EXPECTED , ACTUAL ) assertEqualsDouble(__FILE__, __LINE__, EXPECTED, ACTUAL)
@ -79,4 +84,11 @@ public:
#define TODO_ASSERT_EQUALS( WANTED , CURRENT , ACTUAL ) todoAssertEquals(__FILE__, __LINE__, WANTED, CURRENT, ACTUAL) #define TODO_ASSERT_EQUALS( WANTED , CURRENT , ACTUAL ) todoAssertEquals(__FILE__, __LINE__, WANTED, CURRENT, ACTUAL)
#define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; } #define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance; }
#ifdef _WIN32
#define REQUIRE_LIB( NAME ) { if (!_lib.load("./testrunner", "../cfg/" NAME) && !_lib.load("./testrunner", "cfg/" NAME)) { complainMissingLib(NAME); return; } }
#else
#define REQUIRE_LIB( NAME ) { if (!_lib.load("./testrunner", "cfg/" NAME)) { complainMissingLib(NAME); return; } }
#endif
#define LOAD_LIB( NAME ) { REQUIRE_LIB(NAME); }
#endif #endif