Skip to content

Commit ff22173

Browse files
Fix #958: warn when feof() is used as a while loop condition
feof() only returns true after a read has already failed, causing the loop body to execute once more after the last successful read. Read errors also go undetected since feof() does not distinguish them from EOF. Signed-off-by: Francois Berder <fberder@outlook.fr>
1 parent db9f970 commit ff22173

3 files changed

Lines changed: 49 additions & 0 deletions

File tree

lib/checkio.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,36 @@ void CheckIO::invalidScanfError(const Token *tok)
481481
CWE119, Certainty::normal);
482482
}
483483

484+
void CheckIO::checkWrongfeofUsage()
485+
{
486+
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
487+
488+
logChecker("CheckIO::checkWrongfeofUsage");
489+
490+
for (const Scope * scope : symbolDatabase->functionScopes) {
491+
for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
492+
if (Token::simpleMatch(tok, "while (")) {
493+
const Token* cond = getCondTok(tok);
494+
if (!cond)
495+
continue;
496+
if (Token::Match(cond, "! feof (")) {
497+
wrongfeofUsage(cond);
498+
}
499+
}
500+
}
501+
}
502+
}
503+
504+
void CheckIO::wrongfeofUsage(const Token * tok)
505+
{
506+
reportError(tok, Severity::warning,
507+
"wrongfeofUsage",
508+
"Using feof() as a loop condition may cause the last line to be processed twice.\n"
509+
"feof() returns true only after a read has failed due to end-of-file, so the loop "
510+
"body executes once more after the last successful read. Check the return value of "
511+
"the read function instead (e.g. fgets, fread, fscanf).");
512+
}
513+
484514
//---------------------------------------------------------------------------
485515
// printf("%u", "xyz"); // Wrong argument type
486516
// printf("%u%s", 1); // Too few arguments
@@ -2031,6 +2061,7 @@ void CheckIO::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger)
20312061
checkIO.checkWrongPrintfScanfArguments();
20322062
checkIO.checkCoutCerrMisusage();
20332063
checkIO.checkFileUsage();
2064+
checkIO.checkWrongfeofUsage();
20342065
checkIO.invalidScanf();
20352066
}
20362067

lib/checkio.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class CPPCHECKLIB CheckIO : public Check {
6464
/** @brief scanf can crash if width specifiers are not used */
6565
void invalidScanf();
6666

67+
/** @brief %Check wrong usage of feof */
68+
void checkWrongfeofUsage();
69+
6770
/** @brief %Checks type and number of arguments given to functions like printf or scanf*/
6871
void checkWrongPrintfScanfArguments();
6972

@@ -108,6 +111,7 @@ class CPPCHECKLIB CheckIO : public Check {
108111
void seekOnAppendedFileError(const Token *tok);
109112
void incompatibleFileOpenError(const Token *tok, const std::string &filename);
110113
void invalidScanfError(const Token *tok);
114+
void wrongfeofUsage(const Token *tok);
111115
void wrongPrintfScanfArgumentsError(const Token* tok,
112116
const std::string &functionName,
113117
nonneg int numFormat,

test/testio.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class TestIO : public TestFixture {
4545
TEST_CASE(seekOnAppendedFile);
4646
TEST_CASE(fflushOnInputStream);
4747
TEST_CASE(incompatibleFileOpen);
48+
TEST_CASE(testWrongfeofUsage); // #958
4849

4950
TEST_CASE(testScanf1); // Scanf without field limiters
5051
TEST_CASE(testScanf2);
@@ -743,6 +744,19 @@ class TestIO : public TestFixture {
743744
ASSERT_EQUALS("[test.cpp:3:16]: (warning) The file '\"tmp\"' is opened for read and write access at the same time on different streams [incompatibleFileOpen]\n", errout_str());
744745
}
745746

747+
void testWrongfeofUsage() { // ticket #958
748+
check("void foo() {\n"
749+
" FILE * fp = fopen(\"test.txt\", \"r\");\n"
750+
" while (!feof(fp)) \n"
751+
" {\n"
752+
" char line[100];\n"
753+
" fgets(line, sizeof(line), fp);\n"
754+
" }\n"
755+
" fclose(fp);\n"
756+
"}");
757+
ASSERT_EQUALS("[test.cpp:3:10]: (warning) Using feof() as a loop condition may cause the last line to be processed twice. [wrongfeofUsage]\n", errout_str());
758+
}
759+
746760

747761
void testScanf1() {
748762
check("void foo() {\n"

0 commit comments

Comments
 (0)