Library: Added <minsize> element used for buffer overrun checking

This commit is contained in:
Daniel Marjamäki 2014-07-05 20:31:43 +02:00
parent 88890a851c
commit a3acc3241e
6 changed files with 483 additions and 405 deletions

View File

@ -73,13 +73,6 @@
<arg nr="1"><not-null/><not-uninit/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
</function>
<function name="fgets">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="2"><not-uninit/><not-bool/><valid>0:</valid></arg>
<arg nr="3"><not-null/><not-uninit/></arg>
</function>
<function name="floor"><noreturn>false</noreturn><leak-ignore/><arg nr="1"><not-null/><not-uninit/></arg></function>
<function name="fmod"><noreturn>false</noreturn><leak-ignore/><arg nr="1"><not-null/><not-uninit/></arg><arg nr="2"><not-null/><not-uninit/></arg></function>
<function name="fopen">
@ -135,7 +128,7 @@
<function name="fread">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="1"><not-null/><minsize type="mul" arg="2" arg2="3"/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
<arg nr="3"><not-null/><not-uninit/></arg>
<arg nr="4"><not-null/><not-uninit/></arg>
@ -184,7 +177,7 @@
<function name="fgets">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="1"><not-null/><minsize type="argvalue" arg="2"/></arg>
<arg nr="2"><not-uninit/><not-bool/><valid>0:</valid></arg>
<arg nr="3"><not-null/><not-uninit/></arg>
</function>
@ -196,7 +189,7 @@
<function name="fwrite">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/><not-uninit/></arg>
<arg nr="1"><not-null/><not-uninit/><minsize type="mul" arg="2" arg2="3"/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
<arg nr="3"><not-null/><not-uninit/></arg>
<arg nr="4"><not-null/><not-uninit/></arg>
@ -294,35 +287,35 @@
<function name="memchr">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/><not-uninit/></arg>
<arg nr="1"><not-null/><not-uninit/><minsize type="argvalue" arg="3"/></arg>
<arg nr="2"><not-uninit/><not-bool/><valid>0:</valid></arg>
<arg nr="3"><not-bool/><valid>0:</valid></arg>
</function>
<function name="memcmp">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/><not-uninit/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
<arg nr="1"><not-null/><not-uninit/><minsize type="argvalue" arg="3"/></arg>
<arg nr="2"><not-null/><not-uninit/><minsize type="argvalue" arg="3"/></arg>
<arg nr="3"><not-bool/><valid>0:</valid></arg>
</function>
<function name="memcpy">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
<arg nr="1"><not-null/><minsize type="argvalue" arg="3"/></arg>
<arg nr="2"><not-null/><not-uninit/><minsize type="argvalue" arg="3"/></arg>
<arg nr="3"><not-bool/><valid>0:</valid></arg>
</function>
<function name="memmove">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
<arg nr="1"><not-null/><minsize type="argvalue" arg="3"/></arg>
<arg nr="2"><not-null/><not-uninit/><minsize type="argvalue" arg="3"/></arg>
<arg nr="3"><not-bool/><valid>0:</valid></arg>
</function>
<function name="memset">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="1"><not-null/><minsize type="argvalue" arg="3"/></arg>
<arg nr="2"><not-uninit/></arg>
<arg nr="3"><not-bool/><valid>0:</valid></arg>
</function>
@ -505,7 +498,7 @@
<function name="strcpy">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="1"><not-null/><minsize type="strlen" arg="2"/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
</function>
<function name="wcscpy">
@ -535,14 +528,14 @@
<function name="strncpy">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/></arg>
<arg nr="1"><not-null/><minsize type="sizeof" arg="2"/><minsize type="argvalue" arg="3"/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
<arg nr="3"><not-bool/><valid>0:</valid></arg>
</function>
<function name="strncat">
<noreturn>false</noreturn>
<leak-ignore/>
<arg nr="1"><not-null/><not-uninit/></arg>
<arg nr="1"><not-null/><not-uninit/><minsize type="argvalue" arg="3"/></arg>
<arg nr="2"><not-null/><not-uninit/></arg>
<arg nr="3"><not-bool/><valid>0:</valid></arg>
</function>

View File

@ -264,87 +264,65 @@ static bool bailoutIfSwitch(const Token *tok, const unsigned int varid)
}
//---------------------------------------------------------------------------
void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int par, const ArrayInfo &arrayInfo, const std::list<const Token *>& callstack)
void CheckBufferOverrun::checkFunctionParameter(const Token &ftok, unsigned int par, const ArrayInfo &arrayInfo, const std::list<const Token *>& callstack)
{
// total_size : which parameter in function call takes the total size?
std::map<std::string, unsigned int> total_size;
const std::list<Library::ArgumentChecks::MinSize> * const minsizes = _settings->library.argminsizes(ftok.str(),par);
if (!(Token::simpleMatch(tok.previous(), ".") || Token::Match(tok.tokAt(-2), "!!std ::"))) {
total_size["fgets"] = 2; // The second argument for fgets can't exceed the total size of the array
total_size["memcmp"] = 3;
total_size["memcpy"] = 3;
total_size["memmove"] = 3;
total_size["memchr"] = 3;
if (par == 1) {
// reading from array
// if it is zero terminated properly there won't be buffer overruns
total_size["strncat"] = 3;
total_size["strncpy"] = 3;
total_size["memset"] = 3;
total_size["fread"] = 1001; // parameter 2 * parameter 3
total_size["fwrite"] = 1001; // parameter 2 * parameter 3
}
else if (par == 2) {
if (_settings->standards.posix) {
total_size["read"] = 3;
total_size["pread"] = 3;
total_size["write"] = 3;
total_size["recv"] = 3;
total_size["recvfrom"] = 3;
total_size["send"] = 3;
total_size["sendto"] = 3;
}
}
}
std::map<std::string, unsigned int>::const_iterator it = total_size.find(tok.str());
if (it != total_size.end()) {
if (minsizes && !minsizes->empty() && (!(Token::simpleMatch(ftok.previous(), ".") || Token::Match(ftok.tokAt(-2), "!!std ::")))) {
if (arrayInfo.element_size() == 0)
return;
// arg : the index of the "wanted" argument in the function call.
const unsigned int arg = it->second;
// Parse function call. When a ',' is seen, arg is decremented.
// if arg becomes 1 then the current function parameter is the wanted parameter.
// if arg becomes 1001 then multiply current and next argument.
const Token *tok2 = tok.tokAt(2)->nextArgument();
if (arg == 3)
tok2 = tok2->nextArgument();
if ((arg == 2 || arg == 3) && tok2) {
if (Token::Match(tok2, "%num% ,|)")) {
const MathLib::bigint sz = MathLib::toLongNumber(tok2->str());
MathLib::bigint elements = 1;
MathLib::bigint arraySize = arrayInfo.element_size();
for (unsigned int i = 0; i < arrayInfo.num().size(); ++i)
elements *= arrayInfo.num(i);
if (sz < 0 || sz > int(elements * arrayInfo.element_size())) {
arraySize *= arrayInfo.num(i);
bool error = true;
for (std::list<Library::ArgumentChecks::MinSize>::const_iterator minsize = minsizes->begin(); minsize != minsizes->end(); ++minsize) {
if (!error)
break;
error = false;
const Token *argtok = ftok.tokAt(2);
for (int argnum = 1; argtok && argnum < minsize->arg; argnum++)
argtok = argtok->nextArgument();
if (!argtok)
break;
switch (minsize->type) {
case Library::ArgumentChecks::MinSize::Type::ARGVALUE:
if (Token::Match(argtok, "%num% ,|)")) {
const MathLib::bigint sz = MathLib::toLongNumber(argtok->str());
if (sz > arraySize)
error = true;
} else if (argtok->type() == Token::eChar && Token::Match(argtok->next(), ",|)"))
sizeArgumentAsCharError(argtok);
break;
case Library::ArgumentChecks::MinSize::Type::MUL:
// TODO: handle arbitrary arg2
if (minsize->arg2 == minsize->arg+1 && Token::Match(argtok, "%num% , %num% ,|)")) {
const MathLib::bigint sz = MathLib::toLongNumber(argtok->str()) * MathLib::toLongNumber(argtok->strAt(2));
if (sz > arraySize)
error = true;
}
break;
case Library::ArgumentChecks::MinSize::Type::STRLEN:
if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize)
error = true;
break;
case Library::ArgumentChecks::MinSize::Type::SIZEOF:
if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize)
error = true;
break;
case Library::ArgumentChecks::MinSize::Type::NONE:
break;
};
}
if (error)
bufferOverrunError(callstack, arrayInfo.varname());
}
}
else if (Token::Match(tok2->next(), ",|)") && tok2->type() == Token::eChar) {
sizeArgumentAsCharError(tok2);
}
} else if (arg == 1001) { // special code. This parameter multiplied with the next must not exceed total_size
if (Token::Match(tok2, "%num% , %num% ,|)")) {
const MathLib::bigint sz = MathLib::toLongNumber(MathLib::multiply(tok2->str(), tok2->strAt(2)));
MathLib::bigint elements = 1;
for (unsigned int i = 0; i < arrayInfo.num().size(); ++i)
elements *= arrayInfo.num(i);
if (sz < 0 || sz > int(elements * arrayInfo.element_size())) {
bufferOverrunError(&tok, arrayInfo.varname());
}
}
}
}
// Calling a user function?
// only 1-dimensional arrays can be checked currently
else if (arrayInfo.num().size() == 1) {
const Function* const func = tok.function();
const Function* const func = ftok.function();
if (func && func->hasBody) {
// Get corresponding parameter..
@ -361,38 +339,38 @@ void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int p
return;
// Check the parameter usage in the function scope..
for (const Token* ftok = func->functionScope->classStart; ftok != func->functionScope->classEnd; ftok = ftok->next()) {
if (Token::Match(ftok, "if|for|switch|while (")) {
for (const Token* ftok2 = func->functionScope->classStart; ftok2 != func->functionScope->classEnd; ftok2 = ftok2->next()) {
if (Token::Match(ftok2, "if|for|switch|while (")) {
// bailout if there is buffer usage..
if (bailoutIfSwitch(ftok, parameter->declarationId())) {
if (bailoutIfSwitch(ftok2, parameter->declarationId())) {
break;
}
// no bailout is needed. skip the if-block
else {
// goto end of if block..
ftok = ftok->next()->link()->next()->link();
if (Token::simpleMatch(ftok, "} else {"))
ftok = ftok->linkAt(2);
if (!ftok)
ftok2 = ftok2->linkAt(1)->linkAt(1);
if (Token::simpleMatch(ftok2, "} else {"))
ftok2 = ftok2->linkAt(2);
if (!ftok2)
break;
continue;
}
}
if (ftok->str() == "}")
if (ftok2->str() == "}")
break;
if (ftok->varId() == parameter->declarationId()) {
if (Token::Match(ftok->previous(), "-- %var%") ||
Token::Match(ftok, "%var% --"))
if (ftok2->varId() == parameter->declarationId()) {
if (Token::Match(ftok2->previous(), "-- %var%") ||
Token::Match(ftok2, "%var% --"))
break;
if (Token::Match(ftok->previous(), ";|{|}|%op% %var% [ %num% ]")) {
const MathLib::bigint index = MathLib::toLongNumber(ftok->strAt(2));
if (Token::Match(ftok2->previous(), ";|{|}|%op% %var% [ %num% ]")) {
const MathLib::bigint index = MathLib::toLongNumber(ftok2->strAt(2));
if (index >= 0 && arrayInfo.num(0) > 0 && index >= arrayInfo.num(0)) {
std::list<const Token *> callstack2(callstack);
callstack2.push_back(ftok);
callstack2.push_back(ftok2);
std::vector<MathLib::bigint> indexes;
indexes.push_back(index);
@ -403,10 +381,10 @@ void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int p
}
// Calling function..
if (Token::Match(ftok, "%var% (")) {
if (Token::Match(ftok2, "%var% (")) {
ArrayInfo ai(arrayInfo);
ai.declarationId(parameter->declarationId());
checkFunctionCall(ftok, ai, callstack);
checkFunctionCall(ftok2, ai, callstack);
}
}
}
@ -414,7 +392,7 @@ void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int p
// Check 'float x[10]' arguments in declaration
if (_settings->isEnabled("warning")) {
const Function* const func = tok.function();
const Function* const func = ftok.function();
// If argument is '%type% a[num]' then check bounds against num
if (func) {
@ -439,7 +417,7 @@ void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int p
arraysize *= arrayInfo.num(i);
if (Token::Match(tok2, "[,)]") && arraysize > 0 && argsize > arraysize)
argumentSizeError(&tok, tok.str(), arrayInfo.varname());
argumentSizeError(&ftok, ftok.str(), arrayInfo.varname());
}
}
}

View File

@ -169,6 +169,7 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
bool formatstr = false;
bool strz = false;
std::string valid;
std::list<ArgumentChecks::MinSize> minsizes;
for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) {
if (strcmp(argnode->Name(), "not-bool") == 0)
notbool = true;
@ -206,6 +207,40 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
valid = argnode->GetText();
}
else if (strcmp(argnode->Name(), "minsize") == 0) {
const char *typeattr = argnode->Attribute("type");
if (!typeattr)
return Error(MISSING_ATTRIBUTE, "type");
ArgumentChecks::MinSize::Type type;
if (strcmp(typeattr,"strlen")==0)
type = ArgumentChecks::MinSize::Type::STRLEN;
else if (strcmp(typeattr,"argvalue")==0)
type = ArgumentChecks::MinSize::Type::ARGVALUE;
else if (strcmp(typeattr,"sizeof")==0)
type = ArgumentChecks::MinSize::Type::SIZEOF;
else if (strcmp(typeattr,"mul")==0)
type = ArgumentChecks::MinSize::Type::MUL;
else
return Error(BAD_ATTRIBUTE_VALUE, typeattr);
const char *argattr = argnode->Attribute("arg");
if (!argattr)
return Error(MISSING_ATTRIBUTE, "arg");
if (strlen(argattr) != 1 || argattr[0]<'0' || argattr[0]>'9')
return Error(BAD_ATTRIBUTE_VALUE, argattr);
minsizes.push_back(ArgumentChecks::MinSize(type,argattr[0]-'0'));
if (type == ArgumentChecks::MinSize::Type::MUL) {
const char *arg2attr = argnode->Attribute("arg2");
if (!arg2attr)
return Error(MISSING_ATTRIBUTE, "arg2");
if (strlen(arg2attr) != 1 || arg2attr[0]<'0' || arg2attr[0]>'9')
return Error(BAD_ATTRIBUTE_VALUE, arg2attr);
minsizes.back().arg2 = arg2attr[0] - '0';
}
}
else
return Error(BAD_ATTRIBUTE, argnode->Name());
}
@ -214,7 +249,8 @@ Library::Error Library::load(const tinyxml2::XMLDocument &doc)
argumentChecks[name][nr].notuninit = notuninit;
argumentChecks[name][nr].formatstr = formatstr;
argumentChecks[name][nr].strz = strz;
argumentChecks[name][nr].valid = valid;
argumentChecks[name][nr].valid.swap(valid);
argumentChecks[name][nr].minsizes.swap(minsizes);
} else if (strcmp(functionnode->Name(), "ignorefunction") == 0) {
_ignorefunction.insert(name);
} else if (strcmp(functionnode->Name(), "formatstr") == 0) {

View File

@ -152,6 +152,16 @@ public:
bool formatstr;
bool strz;
std::string valid;
class MinSize {
public:
enum Type {NONE,STRLEN,ARGVALUE,SIZEOF,MUL};
MinSize(Type t, int a) : type(t), arg(a), arg2(0) {}
Type type;
int arg;
int arg2;
};
std::list<MinSize> minsizes;
};
// function name, argument nr => argument data
@ -189,6 +199,24 @@ public:
return arg ? arg->valid : emptyString;
}
bool hasminsize(const std::string &functionName) const {
std::map<std::string, std::map<int, ArgumentChecks> >::const_iterator it1;
it1 = argumentChecks.find(functionName);
if (it1 == argumentChecks.end())
return false;
std::map<int,ArgumentChecks>::const_iterator it2;
for (it2 = it1->second.begin(); it2 != it1->second.end(); ++it2) {
if (!it2->second.minsizes.empty())
return true;
}
return false;
}
const std::list<ArgumentChecks::MinSize> *argminsizes(const std::string &functionName, int argnr) const {
const ArgumentChecks *arg = getarg(functionName, argnr);
return arg ? &arg->minsizes : nullptr;
}
bool markupFile(const std::string &path) const {
return _markupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != _markupExtensions.end();
}

View File

@ -72,6 +72,31 @@ private:
checkBufferOverrun.writeOutsideBufferSize();
}
void checkstd(const char code[], const char filename[] = "test.cpp") {
static bool init;
static Settings settings;
if (!init) {
init = true;
LOAD_LIB_2(settings.library, "std.cfg");
settings.addEnabled("warning");
}
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, filename);
// Clear the error buffer..
errout.str("");
// Check for buffer overruns..
CheckBufferOverrun checkBufferOverrun(&tokenizer, &settings, this);
checkBufferOverrun.bufferOverrun();
checkBufferOverrun.bufferOverrun2();
checkBufferOverrun.arrayIndexThenCheck();
checkBufferOverrun.writeOutsideBufferSize();
}
void run() {
TEST_CASE(noerr1);
TEST_CASE(noerr2);
@ -696,7 +721,7 @@ private:
ASSERT_EQUALS("", errout.str());
// #3168
check("void a(char *p) { memset(p,0,100); }\n"
checkstd("void a(char *p) { memset(p,0,100); }\n"
"void b() {\n"
" char buf[10];\n"
" a(buf);"
@ -2071,14 +2096,7 @@ private:
ASSERT_EQUALS("", errout.str());
}
void buffer_overrun_1_standard_functions() {
check("void f()\n"
"{\n"
" char str[3];\n"
" strcpy(str, \"abc\");\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
void buffer_overrun_1_posix_functions() {
check("void f(int fd)\n"
"{\n"
" char str[3];\n"
@ -2114,50 +2132,6 @@ private:
"}", false, "test.cpp", false);
ASSERT_EQUALS("", errout.str());
check("void f()\n"
"{\n"
" char str[3];\n"
" fgets(str, 3, stdin);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f()\n"
"{\n"
" char str[3];\n"
" fgets(str, 4, stdin);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
// fread
check("void f(FILE* fd)\n"
"{\n"
"char str[3];\n"
"fread(str,1,4,fd);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
check("void f(FILE* fd)\n"
"{\n"
"char str[3];\n"
"fread(str,1,3,fd);\n"
"}");
ASSERT_EQUALS("", errout.str());
// fwrite
check("void f(FILE* fd)\n"
"{\n"
"char str[3];\n"
"fwrite(str,1,4,fd);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
check("void f(FILE* fd)\n"
"{\n"
"char str[3];\n"
"fwrite(str,1,3,fd);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f()\n"
"{\n"
"char str[3];\n"
@ -2185,9 +2159,56 @@ private:
"sendto(s, str, 4, 0, 0x0, 0x0);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
}
void buffer_overrun_1_standard_functions() {
check("void f()\n"
"{\n"
" char str[3];\n"
" strcpy(str, \"abc\");\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
checkstd("void f() {\n"
" char str[3];\n"
" fgets(str, 3, stdin);\n"
"}");
ASSERT_EQUALS("", errout.str());
checkstd("void f() {\n"
" char str[3];\n"
" fgets(str, 4, stdin);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str());
// fread
checkstd("void f(FILE* fd) {\n"
" char str[3];\n"
" fread(str,1,4,fd);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str());
checkstd("void f(FILE* fd) {\n"
" char str[3];\n"
" fread(str,1,3,fd);\n"
"}");
ASSERT_EQUALS("", errout.str());
// fwrite
checkstd("void f(FILE* fd) {\n"
" char str[3];\n"
" fwrite(str,1,4,fd);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str());
checkstd("void f(FILE* fd) {\n"
" char str[3];\n"
" fwrite(str,1,3,fd);\n"
"}");
ASSERT_EQUALS("", errout.str());
// #4968 - not standard function
check("void f() {\n"
checkstd("void f() {\n"
" char str[3];\n"
" foo.memset(str, 0, 100);\n"
" foo::memset(str, 0, 100);\n"
@ -2423,13 +2444,13 @@ private:
void buffer_overrun_13() {
// ticket #836
check("void f() {\n"
checkstd("void f() {\n"
" char a[10];\n"
" memset(a+5, 0, 10);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str());
check("void f() {\n"
checkstd("void f() {\n"
" char a[10];\n"
" memmove(a, a+5, 10);\n"
"}");
@ -2437,21 +2458,21 @@ private:
}
void buffer_overrun_14() {
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = new char[strlen(a)];\n"
" strcpy(b, a);\n"
" return b;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str());
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = new char[strlen(a) + 1];\n"
" strcpy(b, a);\n"
" return b;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = new char[strlen(a)];\n"
" a[0] = '\\0';\n"
" strcpy(b, a);\n"
@ -2459,7 +2480,7 @@ private:
"}");
ASSERT_EQUALS("", errout.str());
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = malloc(strlen(a));\n"
" b = realloc(b, 10000);\n"
" strcpy(b, a);\n"
@ -2467,14 +2488,14 @@ private:
"}");
ASSERT_EQUALS("", errout.str());
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = malloc(strlen(a));\n"
" strcpy(b, a);\n"
" return b;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str());
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = malloc(strlen(a));\n"
" {\n"
" strcpy(b, a);\n"
@ -2483,28 +2504,28 @@ private:
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str());
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = malloc(strlen(a) + 1);\n"
" strcpy(b, a);\n"
" return b;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f(char *a, char *c) {\n"
checkstd("void f(char *a, char *c) {\n"
" char *b = realloc(c, strlen(a));\n"
" strcpy(b, a);\n"
" return b;\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", errout.str());
check("void f(char *a, char *c) {\n"
checkstd("void f(char *a, char *c) {\n"
" char *b = realloc(c, strlen(a) + 1);\n"
" strcpy(b, a);\n"
" return b;\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char *b = malloc(strlen(a));\n"
" sprintf(b, \"%s\", a);\n"
" return b;\n"
@ -2610,7 +2631,7 @@ private:
}
void buffer_overrun_22() { // ticket #3124
check("class A {\n"
checkstd("class A {\n"
"public:\n"
" char b[5][6];\n"
"};\n"
@ -2618,10 +2639,9 @@ private:
" A a;\n"
" memset(a.b, 0, 5 * 6);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("class A {\n"
checkstd("class A {\n"
"public:\n"
" char b[5][6];\n"
"};\n"
@ -2629,23 +2649,22 @@ private:
" A a;\n"
" memset(a.b, 0, 6 * 6);\n"
"}");
ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: a.b\n", errout.str());
}
void buffer_overrun_23() { // ticket #3153
check("void foo() {\n"
checkstd("void foo() {\n"
" double dest = 23.0;\n"
" char* const source = (char*) malloc(sizeof(dest));\n"
" memcpy(&dest, source + sizeof(double), sizeof(dest));\n"
"}", false, "test.cpp", false);
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", errout.str());
"}");
TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", "", errout.str());
check("void foo() {\n"
checkstd("void foo() {\n"
" double dest = 23.0;\n"
" char* const source = (char*) malloc(2 * sizeof(dest));\n"
" memcpy(&dest, source + sizeof(double), sizeof(dest));\n"
"}", false, "test.cpp", false);
"}");
ASSERT_EQUALS("", errout.str());
}
@ -3085,36 +3104,33 @@ private:
}
void strncat1() {
check("void f(char *a, char *b)\n"
"{\n"
checkstd("void f(char *a, char *b) {\n"
" char str[16];\n"
" strncpy(str, a, 10);\n"
" strncat(str, b, 10);\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n", errout.str());
}
void strncat2() {
check("void f(char *a)\n"
"{\n"
" char str[5];\n"
" strncat(str, a, 5);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n", errout.str());
}
void strncat2() {
checkstd("void f(char *a) {\n"
" char str[5];\n"
" strncat(str, a, 5);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n", errout.str());
}
void strncat3() {
check("struct Foo { char a[4]; };\n"
"void f(char *a)\n"
"{\n"
checkstd("struct Foo { char a[4]; };\n"
"void f(char *a) {\n"
" struct Foo x;\n"
" strncat(x.a, a, 5);\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: x.a\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: x.a\n", errout.str());
}
void strncat4() {
check("void f(char *a) {\n"
checkstd("void f(char *a) {\n"
" char str[5];\n"
" strncat(str, \"foobar\", 5);\n"
"}");
@ -3123,20 +3139,18 @@ private:
void strcat1() {
check("struct Foo { char a[4]; };\n"
"void f()\n"
"{\n"
checkstd("struct Foo { char a[4]; };\n"
"void f() {\n"
" struct Foo x;\n"
" strcat(x.a, \"aa\");\n"
" strcat(x.a, \"aa\");\n"
"}");
ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds.\n", errout.str());
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds.\n", errout.str());
}
void strcat2() {
check("struct Foo { char a[5]; };\n"
"void f()\n"
"{\n"
checkstd("struct Foo { char a[5]; };\n"
"void f() {\n"
" struct Foo x;\n"
" strcat(x.a, \"aa\");\n"
" strcat(x.a, \"aa\");\n"
@ -3145,7 +3159,7 @@ private:
}
void strcat3() {
check("void f() {\n"
checkstd("void f() {\n"
" INT str[10];\n"
" strcat(str, \"aa\");\n"
"}");
@ -3155,51 +3169,45 @@ private:
// memchr/memset/memcpy/etc
void memfunc1() {
check("struct S {\n"
checkstd("struct S {\n"
" char a[5];\n"
"};\n"
"void f()\n"
"{\n"
"void f() {\n"
" S s;\n"
" memset(s.a, 0, 10);\n"
"}");
ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: s.a\n", errout.str());
ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: s.a\n", errout.str());
check("void f()\n"
"{\n"
checkstd("void f() {\n"
" char str[5];\n"
" memset(str, 0, 10);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str());
check("void f()\n"
"{\n"
checkstd("void f() {\n"
" char a[5], b[50];\n"
" memcpy(a, b, 10);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str());
check("void f()\n"
"{\n"
checkstd("void f() {\n"
" char a[5], b[50];\n"
" memmove(a, b, 10);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout.str());
// Ticket #909
check("void f()\n"
"{\n"
checkstd("void f() {\n"
" char * pch;\n"
" char str[] = \"Example string\";\n"
" pch = memchr (str, 'p', 16);\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", errout.str());
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str\n", errout.str());
}
// ticket #2121 - buffer access out of bounds when using uint32_t
void memfunc2() {
check("void f()\n"
"{\n"
checkstd("void f() {\n"
" unknown_type_t buf[4];\n"
" memset(buf, 0, 100);\n"
"}");
@ -3208,21 +3216,21 @@ private:
// ticket #1659 - overflowing variable when using memcpy
void memfunc3() {
check("void f() { \n"
"char str1[]=\"Sample string\";\n"
"char str2;\n"
"memcpy (&str2,str1,13);\n" // <-- strlen(str1)+1 = 13
checkstd("void f() { \n"
" char str1[]=\"Sample string\";\n"
" char str2;\n"
" memcpy (&str2,str1,13);\n" // <-- strlen(str1)+1 = 13
"}");
TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: str1\n","", errout.str());
check("void f() {\n"
checkstd("void f() {\n"
" char a[10];\n"
" char str1[] = \"abcdef\";\n"
" memset(a, 0, 11);\n" // <-- strlen(str1) + 5 = 11
"}");
ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: a\n", errout.str());
check("void f() { \n"
checkstd("void f() { \n"
"char str1[]=\"Sample string\";\n"
"char str2;\n"
"memcpy (&str2,str1,15);\n" // <-- strlen(str1) + 1 = 15
@ -3245,7 +3253,7 @@ private:
void varid2() {
check("void foo()\n"
checkstd("void foo()\n"
"{\n"
" char str[10];\n"
" if (str[0])\n"
@ -3401,7 +3409,7 @@ private:
}
void malloc_memset() {
check("void f() {\n"
checkstd("void f() {\n"
" char *p = malloc(10);\n"
" memset(p,0,100);\n"
"}");
@ -3409,18 +3417,18 @@ private:
}
void memset1() {
check("void foo()\n"
checkstd("void foo()\n"
"{\n"
" char s[10];\n"
" memset(s, 5, '*');\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (warning) The size argument is given as a char constant.\n", errout.str());
check("void foo()\n"
checkstd("void foo()\n"
"{\n"
" int* x[5];\n"
" memset(x, 0, sizeof(x));\n"
"}",false,"test.cpp",false);
"}");
ASSERT_EQUALS("", errout.str());
}
@ -3514,33 +3522,32 @@ private:
}
void strncpy1() {
check("void f() {\n"
checkstd("void f() {\n"
" char c[7];\n"
" strncpy(c, \"hello\", 7);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
checkstd("void f() {\n"
" char c[6];\n"
" strncpy(c,\"hello\",6);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
checkstd("void f() {\n"
" char c[5];\n"
" strncpy(c,\"hello\",6);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str());
check("void f() {\n"
checkstd("void f() {\n"
" char c[6];\n"
" strncpy(c,\"hello!\",7);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout.str());
check("struct AB { char a[10]; };\n"
"void foo(AB *ab)\n"
"{\n"
checkstd("struct AB { char a[10]; };\n"
"void foo(AB *ab) {\n"
" strncpy(x, ab->a, 100);\n"
"}");
ASSERT_EQUALS("", errout.str());

View File

@ -34,6 +34,7 @@ private:
TEST_CASE(function_arg);
TEST_CASE(function_arg_any);
TEST_CASE(function_arg_valid);
TEST_CASE(function_arg_minsize);
TEST_CASE(memory);
TEST_CASE(memory2); // define extra "free" allocation functions
TEST_CASE(resource);
@ -158,6 +159,41 @@ private:
ASSERT_EQUALS(false, library.isargvalid("foo", 5, 2));
}
void function_arg_minsize() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"><minsize type=\"strlen\" arg=\"2\"/></arg>\n"
" <arg nr=\"2\"><minsize type=\"argvalue\" arg=\"3\"/></arg>\n"
" </function>\n"
"</def>";
tinyxml2::XMLDocument doc;
doc.Parse(xmldata, sizeof(xmldata));
Library library;
library.load(doc);
// arg1: type=strlen arg2
const std::list<Library::ArgumentChecks::MinSize> *minsizes = library.argminsizes("foo",1);
ASSERT_EQUALS(true, minsizes != nullptr);
ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U);
if (minsizes && minsizes->size() == 1U) {
const Library::ArgumentChecks::MinSize &m = minsizes->front();
ASSERT_EQUALS(Library::ArgumentChecks::MinSize::Type::STRLEN, m.type);
ASSERT_EQUALS(2, m.arg);
}
// arg2: type=argvalue arg3
minsizes = library.argminsizes("foo", 2);
ASSERT_EQUALS(true, minsizes != nullptr);
ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U);
if (minsizes && minsizes->size() == 1U) {
const Library::ArgumentChecks::MinSize &m = minsizes->front();
ASSERT_EQUALS(Library::ArgumentChecks::MinSize::Type::ARGVALUE, m.type);
ASSERT_EQUALS(3, m.arg);
}
}
void memory() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"