diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 512585dea..b655e820a 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -106,6 +106,7 @@ struct Filepointer { nonneg int op_indent; enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX }; AppendMode append_mode; + std::string filename; explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM) : 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"))) { std::string mode; const Token* fileTok = nullptr; + const Token* fileNameTok = nullptr; Filepointer::Operation operation = Filepointer::Operation::NONE; if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" || @@ -187,6 +189,8 @@ void CheckIO::checkFileUsage() mode = "wb+"; fileTok = tok->tokAt(-2); 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%")) { const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument(); 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 filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(OpenMode::UNKNOWN_OM))); } + Filepointer& f = filepointers[fileTok->varId()]; switch (operation) { case Filepointer::Operation::OPEN: + if (fileNameTok) { + for (std::map::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); if (mode.find('a') != std::string::npos) { 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); } +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 diff --git a/lib/checkio.h b/lib/checkio.h index 1f32ed136..8a79e5655 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -114,6 +114,7 @@ private: void writeReadOnlyFileError(const Token *tok); void useClosedFileError(const Token *tok); void seekOnAppendedFileError(const Token *tok); + void incompatibleFileOpenError(const Token *tok, const std::string &filename); void invalidScanfError(const Token *tok); void wrongPrintfScanfArgumentsError(const Token* tok, const std::string &functionName, @@ -145,6 +146,7 @@ private: c.writeReadOnlyFileError(nullptr); c.useClosedFileError(nullptr); c.seekOnAppendedFileError(nullptr); + c.incompatibleFileOpenError(nullptr, "tmp"); c.invalidScanfError(nullptr); c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2); c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr); @@ -174,6 +176,7 @@ private: "- 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" "- 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" "- Invalid usage of output stream. For example: 'std::cout << std::cout;'\n" "- Wrong number of arguments given to 'printf' or 'scanf;'\n"; diff --git a/test/testio.cpp b/test/testio.cpp index bd073108d..b225aa8ca 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -45,6 +45,7 @@ private: TEST_CASE(fileIOwithoutPositioning); TEST_CASE(seekOnAppendedFile); TEST_CASE(fflushOnInputStream); + TEST_CASE(incompatibleFileOpen); TEST_CASE(testScanf1); // Scanf without field limiters TEST_CASE(testScanf2); @@ -715,7 +716,13 @@ private: 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() {