From 0312066b68e8a63d28e22a2b0630022af9e8ddde Mon Sep 17 00:00:00 2001 From: Robert M Date: Sun, 21 Jun 2026 10:45:04 -0400 Subject: [PATCH 1/3] New check: ftell() result is unspecified when file is opened in mode "t". New changes: - Added missing string related to new check - Added checker description for ftellTextModeFile - Updated releasenotes.txt with new check --- .gitignore | 1 + lib/checkio.cpp | 14 ++++++++ lib/checkio.h | 1 + man/checkers/ftellTextModeFile.md | 56 +++++++++++++++++++++++++++++++ releasenotes.txt | 1 + test/testio.cpp | 17 ++++++++++ 6 files changed, 90 insertions(+) create mode 100644 man/checkers/ftellTextModeFile.md diff --git a/.gitignore b/.gitignore index 434203fe2a9..a821204bf2d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.gcno *.gch *.o +*.a *.pyc /cppcheck /cppcheck.exe diff --git a/lib/checkio.cpp b/lib/checkio.cpp index f632ca99e9e..8e4b9849257 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -48,6 +48,7 @@ // CVE ID used: static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE474(474U); // Use of Function with Inconsistent Implementations static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime static const CWE CWE685(685U); // Function Call With Incorrect Number of Arguments static const CWE CWE686(686U); // Function Call With Incorrect Argument Type @@ -111,6 +112,8 @@ namespace { nonneg int op_indent{}; enum class AppendMode : std::uint8_t { UNKNOWN_AM, APPEND, APPEND_EX }; AppendMode append_mode = AppendMode::UNKNOWN_AM; + enum class ReadMode : std::uint8_t { READ_TEXT, READ_BIN }; + ReadMode read_mode = ReadMode::READ_BIN; std::string filename; explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM) : mode(mode_) {} @@ -183,6 +186,7 @@ void CheckIOImpl::checkFileUsage() } } else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { std::string mode; + bool isftell = false; const Token* fileTok = nullptr; const Token* fileNameTok = nullptr; Filepointer::Operation operation = Filepointer::Operation::NONE; @@ -266,6 +270,9 @@ void CheckIOImpl::checkFileUsage() fileTok = tok->tokAt(2); if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok) fileTok = fileTok->nextArgument(); + else if (tok->str() == "ftell") { + isftell = true; + } operation = Filepointer::Operation::UNIMPORTANT; } else if (!Token::Match(tok, "if|for|while|catch|switch") && !mSettings.library.isFunctionConst(tok->str(), true)) { const Token* const end2 = tok->linkAt(1); @@ -321,10 +328,15 @@ void CheckIOImpl::checkFileUsage() f.append_mode = Filepointer::AppendMode::APPEND_EX; else f.append_mode = Filepointer::AppendMode::APPEND; + } + else if (mode.find('r') != std::string::npos && + mode.find('t') != std::string::npos) { + f.read_mode = Filepointer::ReadMode::READ_TEXT; } else f.append_mode = Filepointer::AppendMode::UNKNOWN_AM; f.mode_indent = indent; break; + case Filepointer::Operation::POSITIONING: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); @@ -357,6 +369,8 @@ void CheckIOImpl::checkFileUsage() case Filepointer::Operation::UNIMPORTANT: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); + if (isftell && f.read_mode == Filepointer::ReadMode::READ_TEXT && printPortability) + ftellFileError(tok); break; case Filepointer::Operation::UNKNOWN_OP: f.mode = OpenMode::UNKNOWN_OM; diff --git a/lib/checkio.h b/lib/checkio.h index dff49006bab..ae0ea242dab 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -128,6 +128,7 @@ class CPPCHECKLIB CheckIOImpl : public CheckImpl { void useClosedFileError(const Token *tok); void fcloseInLoopConditionError(const Token *tok, const std::string &varname); void seekOnAppendedFileError(const Token *tok); + void ftellFileError(const Token *tok); void incompatibleFileOpenError(const Token *tok, const std::string &filename); void invalidScanfError(const Token *tok); void wrongfeofUsage(const Token *tok); diff --git a/man/checkers/ftellTextModeFile.md b/man/checkers/ftellTextModeFile.md new file mode 100644 index 00000000000..ce389899085 --- /dev/null +++ b/man/checkers/ftellTextModeFile.md @@ -0,0 +1,56 @@ +# ftellModeTextFile + +**Message**: ftell() result is unspecified when file is opened in mode "t".
+**Category**: Portability
+**Severity**: Style
+**Language**: C/C++ + +## Description + +This checker detects the use of ftell() on a file open in text (or translate) mode. The text mode is not consistent + in between Linux and Windows system and may cause ftell() to return the wrong offset inside a text file. + +This warning helps improve code quality by: +- Making the intent clear that the use of ftell() in "t" mode may cause portability problem. + +## Motivation + +This checker improves portability accross system. + +## How to fix + +According to C11, the file must be opened in binary mode 'b' to prevent this problem. + +Before: +```cpp + FILE *f = fopen("Example.txt", "rt"); + if (f) + { + int position; + struct stat st; + + // Wrong way to get the file size + fseek(f, 0, SEEK_END); + printf( "File size %d\n, ftell(f)); + fclose(f); + } + +``` + +After: +```cpp + + FILE *f = fopen("Example.txt", "rb"); + if (f) + { + fseek(f, 0, SEEK_END); + printf( "Offset %d\n", ftell(f); + fclose(f); + } + +``` + +## Notes + +See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170 + diff --git a/releasenotes.txt b/releasenotes.txt index 2141c34f001..0ca26f2374a 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -6,6 +6,7 @@ Major bug fixes & crashes: New checks: - Warn when feof() is used as a while loop condition (wrongfeofUsage). +- ftell() result is unspecified when file is opened in mode "t". C/C++ support: - diff --git a/test/testio.cpp b/test/testio.cpp index adfb6e31daf..f90773d4cf4 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -44,6 +44,7 @@ class TestIO : public TestFixture { TEST_CASE(fileIOwithoutPositioning); TEST_CASE(seekOnAppendedFile); TEST_CASE(fflushOnInputStream); + TEST_CASE(ftellCompatibility); TEST_CASE(incompatibleFileOpen); TEST_CASE(testWrongfeofUsage); // #958 @@ -728,6 +729,22 @@ class TestIO : public TestFixture { ASSERT_EQUALS("", errout_str()); // #6566 } + void ftellCompatibility() { + + check("void foo() {\n" + " FILE *f = fopen(\"\", \"rt\");\n" + " if (f)\n" + " {\n" + " extern long position;\n" + " fseek(f, 0, SEEK_END);\n" + " position = ftell(f);\n" + " fclose(f);\n" + " }\n" + "}\n", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("[test.cpp:7:21]: (portability) ftell() result is unspecified when file is opened in mode \"t\" [ftellTextModeFile]\n", errout_str()); + } + + void fflushOnInputStream() { check("void foo()\n" "{\n" From 8d08a0d492ef55eb3cbd20f814eec73588723388 Mon Sep 17 00:00:00 2001 From: Robert M Date: Sun, 31 May 2026 10:04:51 -0400 Subject: [PATCH 2/3] Applied comments on manual. --- man/checkers/ftellTextModeFile.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/man/checkers/ftellTextModeFile.md b/man/checkers/ftellTextModeFile.md index ce389899085..6ca6653a6f2 100644 --- a/man/checkers/ftellTextModeFile.md +++ b/man/checkers/ftellTextModeFile.md @@ -1,4 +1,4 @@ -# ftellModeTextFile +# ftellModeTextFile **Message**: ftell() result is unspecified when file is opened in mode "t".
**Category**: Portability
@@ -10,6 +10,10 @@ This checker detects the use of ftell() on a file open in text (or translate) mode. The text mode is not consistent in between Linux and Windows system and may cause ftell() to return the wrong offset inside a text file. +See section 7.21.9.4p2 of the C11 standard regarding the ftell function states for a text stream: + +- For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read. + This warning helps improve code quality by: - Making the intent clear that the use of ftell() in "t" mode may cause portability problem. @@ -26,12 +30,8 @@ Before: FILE *f = fopen("Example.txt", "rt"); if (f) { - int position; - struct stat st; - - // Wrong way to get the file size fseek(f, 0, SEEK_END); - printf( "File size %d\n, ftell(f)); + printf( "File size %d\n", ftell(f)); fclose(f); } @@ -44,7 +44,7 @@ After: if (f) { fseek(f, 0, SEEK_END); - printf( "Offset %d\n", ftell(f); + printf( "File size %d\n", ftell(f)); fclose(f); } From 1a99b391176cce987264d455110e0dc80a1a258f Mon Sep 17 00:00:00 2001 From: Robert M Date: Sun, 21 Jun 2026 11:29:10 -0400 Subject: [PATCH 3/3] One function was deleted, now it's back. --- lib/checkio.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 8e4b9849257..c753dd80739 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -438,6 +438,12 @@ void CheckIOImpl::seekOnAppendedFileError(const Token *tok) "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal); } +void CheckIOImpl::ftellFileError(const Token *tok) +{ + reportError(tok, Severity::portability, + "ftellTextModeFile", "ftell() result is unspecified when file is opened in mode \"t\"", CWE474, Certainty::normal); +} + void CheckIOImpl::incompatibleFileOpenError(const Token *tok, const std::string &filename) { reportError(tok, Severity::warning,