Skip to content

Commit 04091bd

Browse files
authored
Fix #14743 (New check: ftell() result is unspecified when file is opened in mode "t") (#8360)
1 parent ddab076 commit 04091bd

6 files changed

Lines changed: 96 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
*.gcno
33
*.gch
44
*.o
5+
*.a
56
*.pyc
67
/cppcheck
78
/cppcheck.exe

lib/checkio.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
// CVE ID used:
4949
static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer
5050
static const CWE CWE398(398U); // Indicator of Poor Code Quality
51+
static const CWE CWE474(474U); // Use of Function with Inconsistent Implementations
5152
static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime
5253
static const CWE CWE685(685U); // Function Call With Incorrect Number of Arguments
5354
static const CWE CWE686(686U); // Function Call With Incorrect Argument Type
@@ -111,6 +112,8 @@ namespace {
111112
nonneg int op_indent{};
112113
enum class AppendMode : std::uint8_t { UNKNOWN_AM, APPEND, APPEND_EX };
113114
AppendMode append_mode = AppendMode::UNKNOWN_AM;
115+
enum class ReadMode : std::uint8_t { READ_TEXT, READ_BIN };
116+
ReadMode read_mode = ReadMode::READ_BIN;
114117
std::string filename;
115118
explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM)
116119
: mode(mode_) {}
@@ -183,6 +186,7 @@ void CheckIOImpl::checkFileUsage()
183186
}
184187
} else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) {
185188
std::string mode;
189+
bool isftell = false;
186190
const Token* fileTok = nullptr;
187191
const Token* fileNameTok = nullptr;
188192
Filepointer::Operation operation = Filepointer::Operation::NONE;
@@ -266,6 +270,9 @@ void CheckIOImpl::checkFileUsage()
266270
fileTok = tok->tokAt(2);
267271
if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok)
268272
fileTok = fileTok->nextArgument();
273+
else if (tok->str() == "ftell") {
274+
isftell = true;
275+
}
269276
operation = Filepointer::Operation::UNIMPORTANT;
270277
} else if (!Token::Match(tok, "if|for|while|catch|switch") && !mSettings.library.isFunctionConst(tok->str(), true)) {
271278
const Token* const end2 = tok->linkAt(1);
@@ -321,10 +328,15 @@ void CheckIOImpl::checkFileUsage()
321328
f.append_mode = Filepointer::AppendMode::APPEND_EX;
322329
else
323330
f.append_mode = Filepointer::AppendMode::APPEND;
331+
}
332+
else if (mode.find('r') != std::string::npos &&
333+
mode.find('t') != std::string::npos) {
334+
f.read_mode = Filepointer::ReadMode::READ_TEXT;
324335
} else
325336
f.append_mode = Filepointer::AppendMode::UNKNOWN_AM;
326337
f.mode_indent = indent;
327338
break;
339+
328340
case Filepointer::Operation::POSITIONING:
329341
if (f.mode == OpenMode::CLOSED)
330342
useClosedFileError(tok);
@@ -357,6 +369,8 @@ void CheckIOImpl::checkFileUsage()
357369
case Filepointer::Operation::UNIMPORTANT:
358370
if (f.mode == OpenMode::CLOSED)
359371
useClosedFileError(tok);
372+
if (isftell && f.read_mode == Filepointer::ReadMode::READ_TEXT && printPortability)
373+
ftellFileError(tok);
360374
break;
361375
case Filepointer::Operation::UNKNOWN_OP:
362376
f.mode = OpenMode::UNKNOWN_OM;
@@ -424,6 +438,12 @@ void CheckIOImpl::seekOnAppendedFileError(const Token *tok)
424438
"seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal);
425439
}
426440

441+
void CheckIOImpl::ftellFileError(const Token *tok)
442+
{
443+
reportError(tok, Severity::portability,
444+
"ftellTextModeFile", "ftell() result is unspecified when file is opened in mode \"t\"", CWE474, Certainty::normal);
445+
}
446+
427447
void CheckIOImpl::incompatibleFileOpenError(const Token *tok, const std::string &filename)
428448
{
429449
reportError(tok, Severity::warning,

lib/checkio.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ class CPPCHECKLIB CheckIOImpl : public CheckImpl {
128128
void useClosedFileError(const Token *tok);
129129
void fcloseInLoopConditionError(const Token *tok, const std::string &varname);
130130
void seekOnAppendedFileError(const Token *tok);
131+
void ftellFileError(const Token *tok);
131132
void incompatibleFileOpenError(const Token *tok, const std::string &filename);
132133
void invalidScanfError(const Token *tok);
133134
void wrongfeofUsage(const Token *tok);

man/checkers/ftellTextModeFile.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# ftellModeTextFile
2+
3+
**Message**: ftell() result is unspecified when file is opened in mode "t".<br/>
4+
**Category**: Portability<br/>
5+
**Severity**: Style<br/>
6+
**Language**: C/C++
7+
8+
## Description
9+
10+
This checker detects the use of ftell() on a file open in text (or translate) mode. The text mode is not consistent
11+
in between Linux and Windows system and may cause ftell() to return the wrong offset inside a text file.
12+
13+
See section 7.21.9.4p2 of the C11 standard regarding the ftell function states for a text stream:
14+
15+
- 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.
16+
17+
This warning helps improve code quality by:
18+
- Making the intent clear that the use of ftell() in "t" mode may cause portability problem.
19+
20+
## Motivation
21+
22+
This checker improves portability accross system.
23+
24+
## How to fix
25+
26+
According to C11, the file must be opened in binary mode 'b' to prevent this problem.
27+
28+
Before:
29+
```cpp
30+
FILE *f = fopen("Example.txt", "rt");
31+
if (f)
32+
{
33+
fseek(f, 0, SEEK_END);
34+
printf( "File size %d\n", ftell(f));
35+
fclose(f);
36+
}
37+
38+
```
39+
40+
After:
41+
```cpp
42+
43+
FILE *f = fopen("Example.txt", "rb");
44+
if (f)
45+
{
46+
fseek(f, 0, SEEK_END);
47+
printf( "File size %d\n", ftell(f));
48+
fclose(f);
49+
}
50+
51+
```
52+
53+
## Notes
54+
55+
See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170
56+

releasenotes.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Major bug fixes & crashes:
66

77
New checks:
88
- Warn when feof() is used as a while loop condition (wrongfeofUsage).
9+
- ftell() result is unspecified when file is opened in mode "t".
910

1011
C/C++ support:
1112
-

test/testio.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class TestIO : public TestFixture {
4444
TEST_CASE(fileIOwithoutPositioning);
4545
TEST_CASE(seekOnAppendedFile);
4646
TEST_CASE(fflushOnInputStream);
47+
TEST_CASE(ftellCompatibility);
4748
TEST_CASE(incompatibleFileOpen);
4849
TEST_CASE(testWrongfeofUsage); // #958
4950

@@ -728,6 +729,22 @@ class TestIO : public TestFixture {
728729
ASSERT_EQUALS("", errout_str()); // #6566
729730
}
730731

732+
void ftellCompatibility() {
733+
734+
check("void foo() {\n"
735+
" FILE *f = fopen(\"\", \"rt\");\n"
736+
" if (f)\n"
737+
" {\n"
738+
" extern long position;\n"
739+
" fseek(f, 0, SEEK_END);\n"
740+
" position = ftell(f);\n"
741+
" fclose(f);\n"
742+
" }\n"
743+
"}\n", dinit(CheckOptions, $.portability = true));
744+
ASSERT_EQUALS("[test.cpp:7:21]: (portability) ftell() result is unspecified when file is opened in mode \"t\" [ftellTextModeFile]\n", errout_str());
745+
}
746+
747+
731748
void fflushOnInputStream() {
732749
check("void foo()\n"
733750
"{\n"

0 commit comments

Comments
 (0)