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:
parent
599a559351
commit
9841e0ed96
|
@ -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
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue