New check: Warning, if positioning operation (fseek) is performed on a file opened in "a" mode

This commit is contained in:
PKEuS 2014-03-17 11:02:03 +01:00
parent f277d5fb78
commit 86e6bb430a
4 changed files with 55 additions and 14 deletions

View File

@ -72,7 +72,7 @@ void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamN
// fopen("","r"); fwrite(); <- write to read-only file (or vice versa) // fopen("","r"); fwrite(); <- write to read-only file (or vice versa)
// fclose(); fread(); <- Use closed file // fclose(); fread(); <- Use closed file
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
enum OpenMode {CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN}; enum OpenMode { CLOSED, READ_MODE, WRITE_MODE, RW_MODE, UNKNOWN_OM };
static OpenMode getMode(const std::string& str) static OpenMode getMode(const std::string& str)
{ {
if (str.find('+', 1) != std::string::npos) if (str.find('+', 1) != std::string::npos)
@ -81,7 +81,7 @@ static OpenMode getMode(const std::string& str)
return WRITE_MODE; return WRITE_MODE;
else if (str.find('r') != std::string::npos) else if (str.find('r') != std::string::npos)
return READ_MODE; return READ_MODE;
return UNKNOWN; return UNKNOWN_OM;
} }
struct Filepointer { struct Filepointer {
@ -89,8 +89,10 @@ struct Filepointer {
unsigned int mode_indent; unsigned int mode_indent;
enum Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation; enum Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation;
unsigned int op_indent; unsigned int op_indent;
Filepointer(OpenMode mode_ = UNKNOWN) enum AppendMode { UNKNOWN_AM, APPEND, APPEND_EX };
: mode(mode_), mode_indent(0), lastOperation(NONE), op_indent(0) { AppendMode append_mode;
Filepointer(OpenMode mode_ = UNKNOWN_OM)
: mode(mode_), mode_indent(0), lastOperation(NONE), op_indent(0), append_mode(UNKNOWN_AM) {
} }
}; };
@ -113,11 +115,11 @@ void CheckIO::checkFileUsage()
if (var->isLocal()) { if (var->isLocal()) {
if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor" if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor"
filepointers.insert(std::make_pair(var->declarationId(), Filepointer(UNKNOWN))); filepointers.insert(std::make_pair(var->declarationId(), Filepointer(UNKNOWN_OM)));
else else
filepointers.insert(std::make_pair(var->declarationId(), Filepointer(CLOSED))); filepointers.insert(std::make_pair(var->declarationId(), Filepointer(CLOSED)));
} else { } else {
filepointers.insert(std::make_pair(var->declarationId(), Filepointer(UNKNOWN))); filepointers.insert(std::make_pair(var->declarationId(), Filepointer(UNKNOWN_OM)));
// TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode
} }
} }
@ -134,7 +136,7 @@ void CheckIO::checkFileUsage()
for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
if (indent < i->second.mode_indent) { if (indent < i->second.mode_indent) {
i->second.mode_indent = 0; i->second.mode_indent = 0;
i->second.mode = UNKNOWN; i->second.mode = UNKNOWN_OM;
} }
if (indent < i->second.op_indent) { if (indent < i->second.op_indent) {
i->second.op_indent = 0; i->second.op_indent = 0;
@ -144,7 +146,7 @@ void CheckIO::checkFileUsage()
} else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break") { // Reset upon return, continue or break } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break") { // Reset upon return, continue or break
for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
i->second.mode_indent = 0; i->second.mode_indent = 0;
i->second.mode = UNKNOWN; i->second.mode = UNKNOWN_OM;
i->second.op_indent = 0; i->second.op_indent = 0;
i->second.lastOperation = Filepointer::UNKNOWN_OP; i->second.lastOperation = Filepointer::UNKNOWN_OP;
} }
@ -153,7 +155,7 @@ void CheckIO::checkFileUsage()
(windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) { (windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) {
std::map<unsigned int, Filepointer>::iterator i = filepointers.find(tok->varId()); std::map<unsigned int, Filepointer>::iterator i = filepointers.find(tok->varId());
if (i != filepointers.end()) { if (i != filepointers.end()) {
i->second.mode = UNKNOWN; i->second.mode = UNKNOWN_OM;
i->second.lastOperation = Filepointer::UNKNOWN_OP; i->second.lastOperation = Filepointer::UNKNOWN_OP;
} }
} else if (Token::Match(tok, "%var% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { } else if (Token::Match(tok, "%var% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) {
@ -230,18 +232,26 @@ void CheckIO::checkFileUsage()
continue; continue;
if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File
filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(UNKNOWN))); filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(UNKNOWN_OM)));
} }
Filepointer& f = filepointers[fileTok->varId()]; Filepointer& f = filepointers[fileTok->varId()];
switch (operation) { switch (operation) {
case Filepointer::OPEN: case Filepointer::OPEN:
f.mode = getMode(mode); f.mode = getMode(mode);
if (mode.find('a') != std::string::npos) {
if (f.mode == RW_MODE)
f.append_mode = Filepointer::APPEND_EX;
else
f.append_mode = Filepointer::APPEND;
}
f.mode_indent = indent; f.mode_indent = indent;
break; break;
case Filepointer::POSITIONING: case Filepointer::POSITIONING:
if (f.mode == CLOSED) if (f.mode == CLOSED)
useClosedFileError(tok); useClosedFileError(tok);
else if (f.append_mode == Filepointer::APPEND && _settings->isEnabled("warning"))
seekOnAppendedFileError(tok);
break; break;
case Filepointer::READ: case Filepointer::READ:
if (f.mode == CLOSED) if (f.mode == CLOSED)
@ -271,7 +281,7 @@ void CheckIO::checkFileUsage()
useClosedFileError(tok); useClosedFileError(tok);
break; break;
case Filepointer::UNKNOWN_OP: case Filepointer::UNKNOWN_OP:
f.mode = UNKNOWN; f.mode = UNKNOWN_OM;
f.mode_indent = 0; f.mode_indent = 0;
break; break;
default: default:
@ -285,7 +295,7 @@ void CheckIO::checkFileUsage()
} }
for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) { for (std::map<unsigned int, Filepointer>::iterator i = filepointers.begin(); i != filepointers.end(); ++i) {
i->second.op_indent = 0; i->second.op_indent = 0;
i->second.mode = UNKNOWN; i->second.mode = UNKNOWN_OM;
i->second.lastOperation = Filepointer::UNKNOWN_OP; i->second.lastOperation = Filepointer::UNKNOWN_OP;
} }
} }
@ -322,6 +332,12 @@ void CheckIO::useClosedFileError(const Token *tok)
"useClosedFile", "Used file that is not opened."); "useClosedFile", "Used file that is not opened.");
} }
void CheckIO::seekOnAppendedFileError(const Token *tok)
{
reportError(tok, Severity::warning,
"seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.");
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// scanf without field width limits can crash with huge input data // scanf without field width limits can crash with huge input data

View File

@ -100,6 +100,7 @@ private:
void readWriteOnlyFileError(const Token *tok); void readWriteOnlyFileError(const Token *tok);
void writeReadOnlyFileError(const Token *tok); void writeReadOnlyFileError(const Token *tok);
void useClosedFileError(const Token *tok); void useClosedFileError(const Token *tok);
void seekOnAppendedFileError(const Token *tok);
void invalidScanfError(const Token *tok, bool portability); void invalidScanfError(const Token *tok, bool portability);
void wrongPrintfScanfArgumentsError(const Token* tok, void wrongPrintfScanfArgumentsError(const Token* tok,
const std::string &function, const std::string &function,
@ -130,6 +131,7 @@ private:
c.readWriteOnlyFileError(0); c.readWriteOnlyFileError(0);
c.writeReadOnlyFileError(0); c.writeReadOnlyFileError(0);
c.useClosedFileError(0); c.useClosedFileError(0);
c.seekOnAppendedFileError(0);
c.invalidScanfError(0, false); c.invalidScanfError(0, false);
c.wrongPrintfScanfArgumentsError(0,"printf",3,2); c.wrongPrintfScanfArgumentsError(0,"printf",3,2);
c.invalidScanfArgTypeError_s(0, 1, "s", NULL); c.invalidScanfArgTypeError_s(0, 1, "s", NULL);
@ -157,6 +159,7 @@ private:
"* Use a file that has been closed\n" "* Use a file that has been closed\n"
"* File input/output without positioning results in undefined behaviour\n" "* File input/output without positioning results in undefined behaviour\n"
"* Read to a file that has only been opened for writing (or vice versa)\n" "* Read to a file that has only been opened for writing (or vice versa)\n"
"* Repositioning operation on a file opened in append mode\n"
"* Using fflush() on an input stream\n" "* Using fflush() on an input stream\n"
"* Invalid usage of output stream. For example: 'std::cout << std::cout;'\n" "* Invalid usage of output stream. For example: 'std::cout << std::cout;'\n"
"* Wrong number of arguments given to 'printf' or 'scanf;'\n"; "* Wrong number of arguments given to 'printf' or 'scanf;'\n";

View File

@ -37,6 +37,7 @@ private:
TEST_CASE(wrongMode_complex); TEST_CASE(wrongMode_complex);
TEST_CASE(useClosedFile); TEST_CASE(useClosedFile);
TEST_CASE(fileIOwithoutPositioning); TEST_CASE(fileIOwithoutPositioning);
TEST_CASE(seekOnAppendedFile);
TEST_CASE(fflushOnInputStream); TEST_CASE(fflushOnInputStream);
TEST_CASE(testScanf1); // Scanf without field limiters TEST_CASE(testScanf1); // Scanf without field limiters
@ -251,7 +252,8 @@ private:
" rewind(f);\n" " rewind(f);\n"
" fread(buffer, 5, 6, f);\n" " fread(buffer, 5, 6, f);\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); ASSERT_EQUALS("[test.cpp:4]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n"
"[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str());
check("void foo(FILE*& f) {\n" check("void foo(FILE*& f) {\n"
" f = fopen(name, \"a+\");\n" " f = fopen(name, \"a+\");\n"
@ -530,6 +532,26 @@ private:
ASSERT_EQUALS("[test.cpp:4]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str()); ASSERT_EQUALS("[test.cpp:4]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout.str());
} }
void seekOnAppendedFile() {
check("void foo() {\n"
" FILE* f = fopen(\"\", \"a+\");\n"
" fseek(f, 0, SEEK_SET);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" FILE* f = fopen(\"\", \"w\");\n"
" fseek(f, 0, SEEK_SET);\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void foo() {\n"
" FILE* f = fopen(\"\", \"a\");\n"
" fseek(f, 0, SEEK_SET);\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n", errout.str());
}
void fflushOnInputStream() { void fflushOnInputStream() {
check("void foo()\n" check("void foo()\n"
"{\n" "{\n"