Skip to content
32 changes: 32 additions & 0 deletions lib/checkio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,28 @@ void CheckIO::checkFileUsage()
} else if (tok->str() == "fclose") {
fileTok = tok->tokAt(2);
operation = Filepointer::Operation::CLOSE;

// #1473 Check if fclose is in a while loop condition
if (fileTok && fileTok->isVariable()) {
const Token* loopTok = tok->astTop()->previous();

if (loopTok && loopTok->str() == "while") {
const Token* bodyEnd = nullptr;
const Token* bodyStart = nullptr;

if (Token::simpleMatch(loopTok->previous(), "}") && loopTok->previous()->scope()->type == ScopeType::eDo) { // Handle do-while loops
bodyEnd = loopTok->previous();
bodyStart = bodyEnd->link();
} else {
bodyStart = loopTok->linkAt(1)->next();
bodyEnd = bodyStart->link();
}

// Do not trigger a warning if the loop always exits or if the file is opened again in the loop.
if (!isReturnScope(bodyEnd, mSettings->library) && Token::findmatch(bodyStart, "%var% =", bodyEnd, fileTok->varId()) == nullptr)
fcloseInLoopConditionError(tok, fileTok->str());
}
}
} else if (whitelist.find(tok->str()) != whitelist.end()) {
fileTok = tok->tokAt(2);
if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok)
Expand Down Expand Up @@ -392,6 +414,15 @@ void CheckIO::useClosedFileError(const Token *tok)
"useClosedFile", "Used file that is not opened.", CWE910, Certainty::normal);
}

void CheckIO::fcloseInLoopConditionError(const Token *tok, const std::string &varname)
{
reportError(tok, Severity::warning,
"fcloseInLoopCondition",
"fclose() used as loop condition may skip loop body or double-close file handle.\n"
"fclose() closes '" + varname + "' each time it is evaluated. On success the loop body might never execute, on failure fclose() might be called again on the already-closed file handle.",
CWE910, Certainty::normal);
}

void CheckIO::seekOnAppendedFileError(const Token *tok)
{
reportError(tok, Severity::warning,
Expand Down Expand Up @@ -2043,6 +2074,7 @@ void CheckIO::getErrorMessages(ErrorLogger *errorLogger, const Settings *setting
c.readWriteOnlyFileError(nullptr);
c.writeReadOnlyFileError(nullptr);
c.useClosedFileError(nullptr);
c.fcloseInLoopConditionError(nullptr, "fp");
c.seekOnAppendedFileError(nullptr);
c.incompatibleFileOpenError(nullptr, "tmp");
c.invalidScanfError(nullptr);
Expand Down
1 change: 1 addition & 0 deletions lib/checkio.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class CPPCHECKLIB CheckIO : public Check {
void readWriteOnlyFileError(const Token *tok);
void writeReadOnlyFileError(const Token *tok);
void useClosedFileError(const Token *tok);
void fcloseInLoopConditionError(const Token *tok, const std::string &varname);
void seekOnAppendedFileError(const Token *tok);
void incompatibleFileOpenError(const Token *tok, const std::string &filename);
void invalidScanfError(const Token *tok);
Expand Down
24 changes: 23 additions & 1 deletion test/testio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,29 @@ class TestIO : public TestFixture {
" FILE *a = fopen(\"aa\", \"r\");\n"
" while (fclose(a)) {}\n"
"}");
TODO_ASSERT_EQUALS("[test.cpp:3:5]: (error) Used file that is not opened. [useClosedFile]\n", "", errout_str());
ASSERT_EQUALS("[test.cpp:3:12]: (warning) fclose() used as loop condition may skip loop body or double-close file handle. [fcloseInLoopCondition]\n", errout_str());

check("void foo() {\n"
" FILE *a = fopen(\"aa\", \"r\");\n"
" while (fclose(a)) {\n"
" break;\n"
" }\n"
"}");
ASSERT_EQUALS("", errout_str());

check("void foo() {\n"
" FILE *a = fopen(\"aa\", \"r\");\n"
" while (fclose(a)) {\n"
" a = fopen(\"aa\", \"r\");\n"
" }\n"
"}");
ASSERT_EQUALS("", errout_str());

check("void foo() {\n"
" FILE *a = fopen(\"aa\", \"r\");\n"
" do {} while (fclose(a));\n"
"}");
ASSERT_EQUALS("[test.cpp:3:18]: (warning) fclose() used as loop condition may skip loop body or double-close file handle. [fcloseInLoopCondition]\n", errout_str());

// #6823
check("void foo() {\n"
Expand Down
Loading