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:
parent
21d317b7d6
commit
b775714e3d
15
cfg/std.cfg
15
cfg/std.cfg
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue