From 14d63abe65b473ee9b09417a0d2064ba8d6da18c Mon Sep 17 00:00:00 2001 From: Robert M Date: Sat, 21 Mar 2026 12:39:48 -0400 Subject: [PATCH 1/4] ftell() have a different behavior in Windows 11 than before. https://stackoverflow.com/questions/79762122/ftell-no-more-returning-the-correct-offset-on-a-text-file-with-windows-11-ente --- lib/checkio.cpp | 20 ++++++++++++++++++++ lib/checkio.h | 1 + test/testio.cpp | 16 ++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index ee1b4e36c73..dbc698087e9 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -53,6 +53,7 @@ namespace { // 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 @@ -116,6 +117,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_) {} @@ -188,6 +191,7 @@ void CheckIO::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; @@ -249,6 +253,9 @@ void CheckIO::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); @@ -304,10 +311,15 @@ void CheckIO::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); @@ -340,6 +352,8 @@ void CheckIO::checkFileUsage() case Filepointer::Operation::UNIMPORTANT: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); + if (isftell && windows && f.read_mode == Filepointer::ReadMode::READ_TEXT && printPortability) + ftellFileError(tok); break; case Filepointer::Operation::UNKNOWN_OP: f.mode = OpenMode::UNKNOWN_OM; @@ -398,6 +412,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::ftellFileError(const Token *tok) +{ + reportError(tok, Severity::portability, + "ftellTextModeFile", "For a text stream, its file position indicator contains unspecified information. See Section 7.21.9.4p2 of the C11 standard", CWE474, Certainty::normal); +} + void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename) { reportError(tok, Severity::warning, diff --git a/lib/checkio.h b/lib/checkio.h index e37a942770b..273aa6d7949 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -106,6 +106,7 @@ class CPPCHECKLIB CheckIO : public Check { void writeReadOnlyFileError(const Token *tok); void useClosedFileError(const Token *tok); 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 wrongPrintfScanfArgumentsError(const Token* tok, diff --git a/test/testio.cpp b/test/testio.cpp index b402f5c0aa1..411477c417c 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(testScanf1); // Scanf without field limiters @@ -704,6 +705,21 @@ class TestIO : public TestFixture { ASSERT_EQUALS("", errout_str()); // #6566 } + void ftellCompatibility() { + + check("void foo() {\n" + " FILE *f = fopen(\"\", \"rt\");\n" + " if (f)\n" + " {\n" + " fseek(f, 0, SEEK_END);\n" + " (void)ftell(f);\n" + " fclose(f);\n" + " }\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A, $.portability = true)); + ASSERT_EQUALS("[test.cpp:6:16]: (portability) For a text stream, its file position indicator contains unspecified information. See Section 7.21.9.4p2 of the C11 standard [ftellTextModeFile]\n", errout_str()); + } + + void fflushOnInputStream() { check("void foo()\n" "{\n" From 1876e701dfc92db99a5baa1f2f68bbc2955636f0 Mon Sep 17 00:00:00 2001 From: Robert M Date: Sat, 11 Apr 2026 15:01:26 -0400 Subject: [PATCH 2/4] Applied comments. --- lib/checkio.cpp | 5 +++-- test/testio.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index dbc698087e9..64c38522bc9 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -352,7 +352,8 @@ void CheckIO::checkFileUsage() case Filepointer::Operation::UNIMPORTANT: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); - if (isftell && windows && f.read_mode == Filepointer::ReadMode::READ_TEXT && printPortability) + // See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170 + if (isftell && f.read_mode == Filepointer::ReadMode::READ_TEXT && printPortability) ftellFileError(tok); break; case Filepointer::Operation::UNKNOWN_OP: @@ -415,7 +416,7 @@ void CheckIO::seekOnAppendedFileError(const Token *tok) void CheckIO::ftellFileError(const Token *tok) { reportError(tok, Severity::portability, - "ftellTextModeFile", "For a text stream, its file position indicator contains unspecified information. See Section 7.21.9.4p2 of the C11 standard", CWE474, Certainty::normal); + "ftellTextModeFile", "According to Microsoft, the value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. See also 7.21.9.4 in C11 standard.", CWE474, Certainty::normal); } void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename) diff --git a/test/testio.cpp b/test/testio.cpp index 411477c417c..8ff5782c66a 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -715,8 +715,8 @@ class TestIO : public TestFixture { " (void)ftell(f);\n" " fclose(f);\n" " }\n" - "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A, $.portability = true)); - ASSERT_EQUALS("[test.cpp:6:16]: (portability) For a text stream, its file position indicator contains unspecified information. See Section 7.21.9.4p2 of the C11 standard [ftellTextModeFile]\n", errout_str()); + "}\n", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("[test.cpp:6:16]: (portability) According to Microsoft, the value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. See also 7.21.9.4 in C11 standard. [ftellTextModeFile]\n", errout_str()); } From 89e02beb97789c2dbfcda8e1f9cb225fcc23b6be Mon Sep 17 00:00:00 2001 From: Robert M Date: Sat, 18 Apr 2026 12:21:55 -0400 Subject: [PATCH 3/4] Major issue: - Fix the uncrustify check (removed https reference to Microsoft). New changes: - Added missing string related to new check - Added checker description for ftellTextModeFile - Updated copyright. --- .gitignore | 1 + lib/checkio.cpp | 1 - lib/checkio.h | 3 +- man/checkers/ftellTextModeFile.md | 52 +++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) 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 64c38522bc9..afd47f84c1d 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -352,7 +352,6 @@ void CheckIO::checkFileUsage() case Filepointer::Operation::UNIMPORTANT: if (f.mode == OpenMode::CLOSED) useClosedFileError(tok); - // See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170 if (isftell && f.read_mode == Filepointer::ReadMode::READ_TEXT && printPortability) ftellFileError(tok); break; diff --git a/lib/checkio.h b/lib/checkio.h index 273aa6d7949..3151d376aa8 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -1,6 +1,6 @@ /* -*- C++ -*- * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2025 Cppcheck team. + * Copyright (C) 2007-2026 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -141,6 +141,7 @@ class CPPCHECKLIB CheckIO : public Check { "- Missing or wrong width specifiers in 'scanf' format string\n" "- Use a file that has been closed\n" "- File input/output without positioning results in undefined behaviour\n" + "- Using 'ftell' on a file opened in text mode\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" diff --git a/man/checkers/ftellTextModeFile.md b/man/checkers/ftellTextModeFile.md new file mode 100644 index 00000000000..3922c25bce4 --- /dev/null +++ b/man/checkers/ftellTextModeFile.md @@ -0,0 +1,52 @@ +# ftellModeTextFile + +**Message**: According to Microsoft, the value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. See also 7.21.9.4 in C11 standard.
+**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) + { + fseek(f, 0, SEEK_END); + printf( "Offset %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 + From c1038234d670fa31e0bb307538569cce91c75541 Mon Sep 17 00:00:00 2001 From: Robert M Date: Sat, 2 May 2026 14:23:37 -0400 Subject: [PATCH 4/4] Applied latest comment from danmar. --- lib/checkio.cpp | 8 +++++++- lib/checkio.h | 2 +- man/checkers/ftellTextModeFile.md | 11 ++++++++--- test/testio.cpp | 5 +++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index afd47f84c1d..ea9ead62fb9 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -415,7 +415,13 @@ void CheckIO::seekOnAppendedFileError(const Token *tok) void CheckIO::ftellFileError(const Token *tok) { reportError(tok, Severity::portability, - "ftellTextModeFile", "According to Microsoft, the value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. See also 7.21.9.4 in C11 standard.", CWE474, Certainty::normal); + "ftellTextModeFile", "The ftell function obtains the current value of the file position indicator" + " for the stream pointed to by stream. For a binary stream, the value is the number of characters" + " from the beginning of the file. 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. See 7.21.9.4" + " in C11 standard.", CWE474, Certainty::normal); } void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename) diff --git a/lib/checkio.h b/lib/checkio.h index 3151d376aa8..93ecb0f8d69 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -1,6 +1,6 @@ /* -*- C++ -*- * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2026 Cppcheck team. + * Copyright (C) 2007-2025 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/man/checkers/ftellTextModeFile.md b/man/checkers/ftellTextModeFile.md index 3922c25bce4..5a8a90b65a4 100644 --- a/man/checkers/ftellTextModeFile.md +++ b/man/checkers/ftellTextModeFile.md @@ -1,6 +1,6 @@ # ftellModeTextFile -**Message**: According to Microsoft, the value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. See also 7.21.9.4 in C11 standard.
+**Message**: The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. 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. See a7.21.9.4 in C11 standard.
**Category**: Portability
**Severity**: Style
**Language**: C/C++ @@ -26,8 +26,13 @@ Before: FILE *f = fopen("Example.txt", "rt"); if (f) { - fseek(f, 0, SEEK_END); - printf( "Offset %d\n", ftell(f); + int position; + struct stat st; + + position = fseek(f, 0, SEEK_END); + fstat(f, &st); + printf( "Position %d\n", ftell(f); + printf( "File size %d\n, st.st_size); fclose(f); } diff --git a/test/testio.cpp b/test/testio.cpp index 8ff5782c66a..06176c6c07a 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -711,12 +711,13 @@ class TestIO : public TestFixture { " FILE *f = fopen(\"\", \"rt\");\n" " if (f)\n" " {\n" + " extern long position;\n" " fseek(f, 0, SEEK_END);\n" - " (void)ftell(f);\n" + " position = ftell(f);\n" " fclose(f);\n" " }\n" "}\n", dinit(CheckOptions, $.portability = true)); - ASSERT_EQUALS("[test.cpp:6:16]: (portability) According to Microsoft, the value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-line feed translation. See also 7.21.9.4 in C11 standard. [ftellTextModeFile]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:7:21]: (portability) The ftell function obtains the current value of the file position indicator for the stream pointed to by stream. For a binary stream, the value is the number of characters from the beginning of the file. 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. See 7.21.9.4 in C11 standard. [ftellTextModeFile]\n", errout_str()); }