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="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="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>

View File

@ -9,4 +9,18 @@
<noreturn>false</noreturn>
</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>

View File

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

View File

@ -186,6 +186,10 @@ bool Library::load(const tinyxml2::XMLDocument &doc)
argumentChecks[name][nr].valid = valid;
} else if (strcmp(functionnode->Name(), "ignorefunction") == 0) {
_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
return false;
}

View File

@ -84,6 +84,18 @@ public:
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> leakignore;
@ -325,7 +337,8 @@ private:
std::map<std::string, CodeBlock> _executableblocks; // keywords for blocks of executable code
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::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 {

View File

@ -53,11 +53,9 @@ private:
TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920
TEST_CASE(testMicrosoftSecurePrintfArgument);
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..
errout.str("");
@ -69,8 +67,7 @@ private:
settings.inconclusive = inconclusive;
settings.platform(platform);
if (lib)
settings.library = *lib;
settings.library = _lib;
// Tokenize..
Tokenizer tokenizer(&settings, this);
@ -437,6 +434,8 @@ private:
void testScanf1() {
LOAD_LIB("std.cfg");
check("void foo() {\n"
" int a, b;\n"
" FILE *file = fopen(\"test\", \"r\");\n"
@ -455,6 +454,8 @@ private:
}
void testScanf2() {
LOAD_LIB("std.cfg");
check("void foo() {\n"
" scanf(\"%5s\", bar);\n" // Width specifier given
" scanf(\"%5[^~]\", bar);\n" // Width specifier given
@ -481,6 +482,7 @@ private:
}
void testScanf4() { // ticket #2553
LOAD_LIB("std.cfg");
check("void f()\n"
"{\n"
@ -494,6 +496,8 @@ private:
void testScanfArgument() {
LOAD_LIB("std.cfg");
check("void foo() {\n"
" scanf(\"%1d\", &foo);\n"
" sscanf(bar, \"%1d\", &foo);\n"
@ -1322,6 +1326,7 @@ private:
}
void testPrintfArgument() {
LOAD_LIB("std.cfg");
check("void foo() {\n"
" printf(\"%u\");\n"
" printf(\"%u%s\", 123);\n"
@ -2107,6 +2112,8 @@ private:
}
void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings
LOAD_LIB("std.cfg");
check("void foo() {"
" int bar;"
" printf(\"%1$d\", 1);"
@ -2131,6 +2138,9 @@ private:
void testMicrosoftPrintfArgument() {
LOAD_LIB("std.cfg");
LOAD_LIB("windows.cfg");
check("void foo() {\n"
" size_t s;\n"
" ptrdiff_t p;\n"
@ -2220,6 +2230,9 @@ private:
}
void testMicrosoftScanfArgument() {
LOAD_LIB("std.cfg");
LOAD_LIB("windows.cfg");
check("void foo() {\n"
" size_t s;\n"
" ptrdiff_t p;\n"
@ -2324,6 +2337,9 @@ private:
}
void testMicrosoftSecurePrintfArgument() {
LOAD_LIB("std.cfg");
LOAD_LIB("windows.cfg");
check("void foo() {\n"
" int i;\n"
" unsigned int u;\n"
@ -2514,6 +2530,8 @@ private:
}
void testMicrosoftSecureScanfArgument() {
LOAD_LIB("windows.cfg");
check("void foo() {\n"
" int i;\n"
" unsigned int u;\n"
@ -2644,22 +2662,6 @@ private:
"}\n", false, false, Settings::Win32W);
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)

View File

@ -62,6 +62,7 @@ unsigned int TestFixture::countTests;
std::size_t TestFixture::fails_counter = 0;
std::size_t TestFixture::todos_counter = 0;
std::size_t TestFixture::succeeded_todos_counter = 0;
std::set<std::string> TestFixture::missingLibs;
TestFixture::TestFixture(const std::string &_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)
{
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 << 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();
return fails_counter;
}

View File

@ -21,8 +21,10 @@
#define testsuiteH
#include <sstream>
#include <set>
#include "errorlogger.h"
#include "redirect.h"
#include "library.h"
class options;
@ -33,8 +35,10 @@ private:
static std::size_t fails_counter;
static std::size_t todos_counter;
static std::size_t succeeded_todos_counter;
static std::set<std::string> missingLibs;
protected:
Library _lib;
std::string classname;
std::string testToRun;
bool gcc_style_errors;
@ -57,6 +61,7 @@ protected:
void todoAssertEquals(const char *filename, unsigned int linenr, long long wanted,
long long current, long long actual) const;
void assertThrowFail(const char *filename, unsigned int linenr) const;
void complainMissingLib(const char* libname) const;
void processOptions(const options& args);
public:
virtual void reportOut(const std::string &outmsg);
@ -70,7 +75,7 @@ public:
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_EQUALS( EXPECTED , ACTUAL ) assertEquals(__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 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