new check; file can not be opened for read and write access at the same time on different streams (misra rule 22.3)

This commit is contained in:
Daniel Marjamäki 2021-07-10 13:59:47 +02:00
parent 599a559351
commit 9841e0ed96
3 changed files with 32 additions and 1 deletions

View File

@ -106,6 +106,7 @@ struct Filepointer {
nonneg int op_indent; nonneg int op_indent;
enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX }; enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX };
AppendMode append_mode; AppendMode append_mode;
std::string filename;
explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM) explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM)
: mode(mode_), mode_indent(0), lastOperation(Operation::NONE), op_indent(0), append_mode(AppendMode::UNKNOWN_AM) { : mode(mode_), mode_indent(0), lastOperation(Operation::NONE), op_indent(0), append_mode(AppendMode::UNKNOWN_AM) {
} }
@ -174,6 +175,7 @@ void CheckIO::checkFileUsage()
} else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { } else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) {
std::string mode; std::string mode;
const Token* fileTok = nullptr; const Token* fileTok = nullptr;
const Token* fileNameTok = nullptr;
Filepointer::Operation operation = Filepointer::Operation::NONE; Filepointer::Operation operation = Filepointer::Operation::NONE;
if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" || if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" ||
@ -187,6 +189,8 @@ void CheckIO::checkFileUsage()
mode = "wb+"; mode = "wb+";
fileTok = tok->tokAt(-2); fileTok = tok->tokAt(-2);
operation = Filepointer::Operation::OPEN; operation = Filepointer::Operation::OPEN;
if (Token::Match(tok, "fopen ( %str% ,"))
fileNameTok = tok->tokAt(2);
} else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name%")) { } else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name%")) {
const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument(); const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument();
if (modeTok && modeTok->tokType() == Token::eString) if (modeTok && modeTok->tokType() == Token::eString)
@ -266,10 +270,21 @@ void CheckIO::checkFileUsage()
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(OpenMode::UNKNOWN_OM))); filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(OpenMode::UNKNOWN_OM)));
} }
Filepointer& f = filepointers[fileTok->varId()]; Filepointer& f = filepointers[fileTok->varId()];
switch (operation) { switch (operation) {
case Filepointer::Operation::OPEN: case Filepointer::Operation::OPEN:
if (fileNameTok) {
for (std::map<int, Filepointer>::const_iterator it = filepointers.cbegin(); it != filepointers.cend(); ++it) {
const Filepointer &fptr = it->second;
if (fptr.filename == fileNameTok->str() && (fptr.mode == OpenMode::RW_MODE || fptr.mode == OpenMode::WRITE_MODE))
incompatibleFileOpenError(tok, fileNameTok->str());
}
f.filename = fileNameTok->str();
}
f.mode = getMode(mode); f.mode = getMode(mode);
if (mode.find('a') != std::string::npos) { if (mode.find('a') != std::string::npos) {
if (f.mode == OpenMode::RW_MODE) if (f.mode == OpenMode::RW_MODE)
@ -370,6 +385,12 @@ void CheckIO::seekOnAppendedFileError(const Token *tok)
"seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal); "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal);
} }
void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename)
{
reportError(tok, Severity::warning,
"incompatibleFileOpen", "The file '" + filename + "' is opened for read and write access at the same time on different streams", CWE664, Certainty::normal);
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// scanf without field width limits can crash with huge input data // scanf without field width limits can crash with huge input data

View File

@ -114,6 +114,7 @@ private:
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 seekOnAppendedFileError(const Token *tok);
void incompatibleFileOpenError(const Token *tok, const std::string &filename);
void invalidScanfError(const Token *tok); void invalidScanfError(const Token *tok);
void wrongPrintfScanfArgumentsError(const Token* tok, void wrongPrintfScanfArgumentsError(const Token* tok,
const std::string &functionName, const std::string &functionName,
@ -145,6 +146,7 @@ private:
c.writeReadOnlyFileError(nullptr); c.writeReadOnlyFileError(nullptr);
c.useClosedFileError(nullptr); c.useClosedFileError(nullptr);
c.seekOnAppendedFileError(nullptr); c.seekOnAppendedFileError(nullptr);
c.incompatibleFileOpenError(nullptr, "tmp");
c.invalidScanfError(nullptr); c.invalidScanfError(nullptr);
c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2); c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2);
c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr); c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr);
@ -174,6 +176,7 @@ private:
"- 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" "- Repositioning operation on a file opened in append mode\n"
"- The same file can't be open for read and write at the same time on different streams\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

@ -45,6 +45,7 @@ private:
TEST_CASE(fileIOwithoutPositioning); TEST_CASE(fileIOwithoutPositioning);
TEST_CASE(seekOnAppendedFile); TEST_CASE(seekOnAppendedFile);
TEST_CASE(fflushOnInputStream); TEST_CASE(fflushOnInputStream);
TEST_CASE(incompatibleFileOpen);
TEST_CASE(testScanf1); // Scanf without field limiters TEST_CASE(testScanf1); // Scanf without field limiters
TEST_CASE(testScanf2); TEST_CASE(testScanf2);
@ -715,7 +716,13 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void incompatibleFileOpen() {
check("void foo() {\n"
" FILE *f1 = fopen(\"tmp\", \"wt\");\n"
" FILE *f2 = fopen(\"tmp\", \"rt\");\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning) The file '\"tmp\"' is opened for read and write access at the same time on different streams\n", errout.str());
}
void testScanf1() { void testScanf1() {