Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
*.gcno
*.gch
*.o
*.a
*.pyc
/cppcheck
/cppcheck.exe
Expand Down
14 changes: 14 additions & 0 deletions lib/checkio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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_) {}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lib/checkio.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,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 wrongPrintfScanfArgumentsError(const Token* tok,
Expand Down
56 changes: 56 additions & 0 deletions man/checkers/ftellTextModeFile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# ftellModeTextFile

**Message**: ftell() result is unspecified when file is opened in mode "t".<br/>
**Category**: Portability<br/>
**Severity**: Style<br/>
**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
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like that we provide the text from the standard . You only reference the windows documentation, that is not as strong and important as the standard. and also I feel it's misleading to point at windows this is an issue you should be aware of no matter what platform you compile on.

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.

## 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( "File size %d\n", ftell(f));
fclose(f);
}
Comment thread
danmar marked this conversation as resolved.

```

After:
```cpp

FILE *f = fopen("Example.txt", "rb");
if (f)
{
fseek(f, 0, SEEK_END);
printf( "File size %d\n", ftell(f));
fclose(f);
}

```

## Notes

See https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170

1 change: 1 addition & 0 deletions releasenotes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ New checks:
- funcArgNamesDifferentUnnamed warns on function declarations/definitions where a parameter in either location is unnamed
- uninitMemberVarNoCtor warns on user-defined types where (1) some but not all members requiring initialization have in-class initializers or (2) there is a mixture of members which do/do not require initialization.
- fcloseInLoopCondition warns when fclose() is used as a while loop condition, which may skip the loop body or double-close the file handle.
- ftell() result is unspecified when file is opened in mode "t".

C/C++ support:
-
Expand Down
17 changes: 17 additions & 0 deletions test/testio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -727,6 +728,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"
Expand Down