From 4ca36afcb7956c66e42abd0c1f81e93e491a9c5b Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Fri, 5 Aug 2022 23:35:21 +0200 Subject: [PATCH 01/31] Package IO4 --- rule_packages/c/IO1.json | 4 +- rule_packages/c/IO4.json | 50 +++++++++++++++++++ rules.csv | 8 +-- .../generate_package_description.py | 5 +- 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 rule_packages/c/IO4.json diff --git a/rule_packages/c/IO1.json b/rule_packages/c/IO1.json index 481843d7ff..e6576be906 100644 --- a/rule_packages/c/IO1.json +++ b/rule_packages/c/IO1.json @@ -65,7 +65,7 @@ }, "queries": [ { - "description": "", + "description": "Do not alternate input and output operations on a file without an intervening flush or positioning call.", "kind": "problem", "name": "Do not alternately input and output from a stream without an intervening flush or positioning call", "precision": "very-high", @@ -88,7 +88,7 @@ }, "queries": [ { - "description": "", + "description": "Close files when they are no longer needed", "kind": "problem", "name": "Close files when they are no longer needed", "precision": "very-high", diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json new file mode 100644 index 0000000000..ac1f49cd3a --- /dev/null +++ b/rule_packages/c/IO4.json @@ -0,0 +1,50 @@ +{ + "CERT-C": { + "FIO45-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Avoid TOCTOU race conditions accessing files", + "kind": "problem", + "name": "Avoid TOCTOU race conditions while accessing files", + "precision": "very-high", + "severity": "error", + "short_name": "ToctouRaceConditionsWhileAccessingFiles", + "tags": [ + "correctness", + "security" + ] + } + ], + "title": "Avoid TOCTOU race conditions while accessing files", + "implementation_scope": { + "description": "None" + } + }, + "FIO47-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Use valid format strings", + "kind": "problem", + "name": "Use valid format strings", + "precision": "high", + "severity": "error", + "short_name": "UseValidFormatStrings", + "tags": [ + "correctness", + "security" + ] + } + ], + "title": "Use valid format strings", + "implementation_scope": { + "description": "None" + } + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 44d2dcf85a..a4b7d16efa 100755 --- a/rules.csv +++ b/rules.csv @@ -540,9 +540,9 @@ c,CERT-C,FIO40-C,Yes,Rule,,,Reset strings on fgets() or fgetws() failure,,IO2,Me c,CERT-C,FIO41-C,Yes,Rule,,,"Do not call getc(), putc(), getwc(), or putwc() with a stream argument that has side effects",,IO2,Medium, c,CERT-C,FIO42-C,Yes,Rule,,,Close files when they are no longer needed,FIO51-CPP,IO1,Medium, c,CERT-C,FIO44-C,Yes,Rule,,,Only use values for fsetpos() that are returned from fgetpos(),,IO2,Medium, -c,CERT-C,FIO45-C,Yes,Rule,,,Avoid TOCTOU race conditions while accessing files,,IO,Medium, +c,CERT-C,FIO45-C,Yes,Rule,,,Avoid TOCTOU race conditions while accessing files,,IO4,Medium, c,CERT-C,FIO46-C,Yes,Rule,,,Do not access a closed file,FIO51-CPP,IO1,Hard, -c,CERT-C,FIO47-C,Yes,Rule,,,Use valid format strings,,IO,Hard, +c,CERT-C,FIO47-C,Yes,Rule,,,Use valid format strings,,IO4,Hard, c,CERT-C,FLP30-C,Yes,Rule,,,Do not use floating-point variables as loop counters,,Statements,Easy, c,CERT-C,FLP32-C,Yes,Rule,,,Prevent or detect domain and range errors in math functions,A0-4-4,Types,Medium, c,CERT-C,FLP34-C,Yes,Rule,,,Ensure that floating-point conversions are within range of the new type,,Types,Medium, @@ -761,8 +761,8 @@ c,MISRA-C-2012,RULE-21-15,Yes,Required,,,"The pointer arguments to the Standard c,MISRA-C-2012,RULE-21-16,Yes,Required,,,"The pointer arguments to the Standard Library function memcmp shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type",,Types,Medium, c,MISRA-C-2012,RULE-21-17,Yes,Mandatory,,,Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters,,Memory,Hard, c,MISRA-C-2012,RULE-21-18,Yes,Mandatory,,,The size_t argument passed to any function in shall have an appropriate value,,Expressions,Medium, -c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts,Easy, -c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV31-C, ENV34-C",Contracts,Easy, +c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts1,Easy, +c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV31-C, ENV34-C",Contracts1,Easy, c,MISRA-C-2012,RULE-21-21,Yes,Required,,,The Standard Library function system of shall not be used,ENV33-C,Banned,Import, c,MISRA-C-2012,RULE-22-1,Yes,Required,,,All resources obtained dynamically by means of Standard Library functions shall be explicitly released,,Memory,Hard, c,MISRA-C-2012,RULE-22-2,Yes,Mandatory,,,A block of memory shall only be freed if it was allocated by means of a Standard Library function,,Memory,Hard, diff --git a/scripts/generate_rules/generate_package_description.py b/scripts/generate_rules/generate_package_description.py index d3890e230f..51fb63a97d 100644 --- a/scripts/generate_rules/generate_package_description.py +++ b/scripts/generate_rules/generate_package_description.py @@ -166,7 +166,10 @@ def generate_short_name(title): "kind" : "problem", "tags" : [] } - ] + ], + "implementation_scope": { + "description": "None." + } } if not package_description: From 70c643a1d09a59d64ff68282b89dd736263dd55b Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 9 Aug 2022 11:18:37 +0200 Subject: [PATCH 02/31] Rule FIO45-C --- .vscode/tasks.json | 1 + ...ToctouRaceConditionsWhileAccessingFiles.md | 162 ++++++++++++++++++ ...ToctouRaceConditionsWhileAccessingFiles.ql | 48 ++++++ ...RaceConditionsWhileAccessingFiles.expected | 1 + c/cert/test/rules/FIO45-C/test.c | 85 +++++++++ .../cpp/standardlibrary/FileAccess.qll | 31 +++- rule_packages/c/IO4.json | 4 +- 7 files changed, 325 insertions(+), 7 deletions(-) create mode 100644 c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md create mode 100644 c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql create mode 100644 c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected create mode 100644 c/cert/test/rules/FIO45-C/test.c diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 84d63e64d7..906b085c24 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -210,6 +210,7 @@ "IO1", "IO2", "IO3", + "IO4", "Includes", "Initialization", "IntegerConversion", diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md new file mode 100644 index 0000000000..f4932ad2e2 --- /dev/null +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md @@ -0,0 +1,162 @@ +# FIO45-C: Avoid TOCTOU race conditions while accessing files + +This query implements the CERT-C rule FIO45-C: + +> Avoid TOCTOU race conditions while accessing files + + +## Description + +A [TOCTOU](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-TOCTOU) (time-of-check, time-of-use) race condition is possible when two or more concurrent processes are operating on a shared file system \[[Seacord 2013b](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Seacord2013)\]. Typically, the first access is a check to verify some attribute of the file, followed by a call to use the file. An attacker can alter the file between the two accesses, or replace the file with a symbolic or hard link to a different file. These TOCTOU conditions can be exploited when a program performs two or more file operations on the same file name or path name. + +A program that performs two or more file operations on a single file name or path name creates a race window between the two file operations. This race window comes from the assumption that the file name or path name refers to the same resource both times. If an attacker can modify the file, remove it, or replace it with a different file, then this assumption will not hold. + +## Noncompliant Code Example + +If an existing file is opened for writing with the `w` mode argument, the file's previous contents (if any) are destroyed. This noncompliant code example tries to prevent an existing file from being overwritten by first opening it for reading before opening it for writing. An attacker can exploit the race window between the two calls to `fopen()` to overwrite an existing file. + +```cpp +#include + +void open_some_file(const char *file) { + FILE *f = fopen(file, "r"); + if (NULL != f) { + /* File exists, handle error */ + } else { + if (fclose(f) == EOF) { + /* Handle error */ + } + f = fopen(file, "w"); + if (NULL == f) { + /* Handle error */ + } + + /* Write to file */ + if (fclose(f) == EOF) { + /* Handle error */ + } + } +} + +``` + +## Compliant Solution + +This compliant solution invokes `fopen()` at a single location and uses the `x` mode of `fopen()`, which was added in C11. This mode causes `fopen()` to fail if the file exists. This check and subsequent open is performed without creating a race window. The `x` mode provides exclusive access to the file only if the host environment provides this support. + +```cpp +#include + +void open_some_file(const char *file) { + FILE *f = fopen(file, "wx"); + if (NULL == f) { + /* Handle error */ + } + /* Write to file */ + if (fclose(f) == EOF) { + /* Handle error */ + } +} +``` + +## Compliant Solution (POSIX) + +This compliant solution uses the `O_CREAT` and `O_EXCL` flags of POSIX's `open()` function. These flags cause `open()` to fail if the file exists. + +```cpp +#include +#include +#include + +void open_some_file(const char *file) { + int fd = open(file, O_CREAT | O_EXCL | O_WRONLY); + if (-1 != fd) { + FILE *f = fdopen(fd, "w"); + if (NULL != f) { + /* Write to file */ + + if (fclose(f) == EOF) { + /* Handle error */ + } + } else { + if (close(fd) == -1) { + /* Handle error */ + } + } + } +} +``` + +## Exceptions + +**FIO45-C-EX2:** Accessing a file name or path name multiple times is permitted if the file referenced resides in a secure directory. (For more information, see [FIO15-C. Ensure that file operations are performed in a secure directory](https://wiki.sei.cmu.edu/confluence/display/c/FIO15-C.+Ensure+that+file+operations+are+performed+in+a+secure+directory).) + +**FIO45-C-EX3:** Accessing a file name or path name multiple times is permitted if the program can verify that every operation operates on the same file. + +This POSIX code example verifies that each subsequent file access operates on the same file. In POSIX, every file can be uniquely identified by using its device and i-node attributes. This code example checks that a file name refers to a regular file (and not a directory, symbolic link, or other special file) by invoking `lstat()`. This call also retrieves its device and i-node. The file is subsequently opened. Finally, the program verifies that the file that was opened is the same one (matching device and i-nodes) as the file that was confirmed as a regular file. + +An attacker can still exploit this code if they have the ability to delete the benign file and create the malicious file within the race window between lstat() and open(). It is possible that the OS kernel will reuse the same device and i-node for both files. This can be mitigated by making sure that the attacker lacks the permissions to delete the benign file. + +```cpp +#include +#include + +int open_regular_file(char *filename, int flags) { + struct stat lstat_info; + struct stat fstat_info; + int f; + + if (lstat(filename, &lstat_info) == -1) { + /* File does not exist, handle error */ + } + + if (!S_ISREG(lstat_info.st_mode)) { + /* File is not a regular file, handle error */ + } + + if ((f = open(filename, flags)) == -1) { + /* File has disappeared, handle error */ + } + + if (fstat(f, &fstat_info) == -1) { + /* Handle error */ + } + + if (lstat_info.st_ino != fstat_info.st_ino || + lstat_info.st_dev != fstat_info.st_dev) { + /* Open file is not the expected regular file, handle error */ + } + + /* f is the expected regular open file */ + return f; +} +``` + +## Risk Assessment + +[TOCTOU](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-TOCTOU) race conditions can result in [unexpected behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior), including privilege escalation. + +
Rule Severity Likelihood Remediation Cost Priority Level
FIO45-C High Probable High P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
CodeSonar 7.0p0 IO.RACE File system race condition
Coverity 2017.07 TOCTOU Implemented
Helix QAC 2022.2 C4851, C4852, C4853 C++4851, C++4852, C++4853
Klocwork 2022.2 SV.TOCTOU.FILE_ACCESS
LDRA tool suite 9.7.1 75 D Partially implemented
Parasoft C/C++test 2022.1 CERT_C-FIO45-a Avoid race conditions while accessing files
Polyspace Bug Finder R2022a CERT C: Rule FIO45-C Checks for file access between time of check and use (rule fully covered)
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO45-C). + +## Bibliography + +
\[ Seacord 2013b \] Chapter 7, "Files"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FIO45-C: Avoid TOCTOU race conditions while accessing files](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql new file mode 100644 index 0000000000..5b329c2545 --- /dev/null +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql @@ -0,0 +1,48 @@ +/** + * @id c/cert/toctou-race-conditions-while-accessing-files + * @name FIO45-C: Avoid TOCTOU race conditions while accessing files + * @description Avoid TOCTOU race conditions accessing files + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio45-c + * correctness + * security + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import codingstandards.cpp.standardlibrary.FileAccess +import codingstandards.cpp.ReadErrorsAndEOF +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** + * A function call that opens a file as read-only + * but does not read the content of the file. + */ +class EmptyFOpenCall extends FOpenCall { + EmptyFOpenCall() { + this.isReadOnlyMode() and + // the FILE is only used as argument to close or in a NULL check + not exists(Expr x | + this != x and + DataFlow::localExprFlow(this, x) and + not closed(x) and + exists(EQExpr eq | + eq.getAnOperand() = x and eq.getAnOperand() = any(NULLMacro m).getAnInvocation().getExpr() + ) + ) + } +} + +// The same file is opened multiple times in different modes +from EmptyFOpenCall emptyFopen, FOpenCall fopen +where + not isExcluded(emptyFopen, IO4Package::toctouRaceConditionsWhileAccessingFilesQuery()) and + not fopen.isReadOnlyMode() and + globalValueNumber(emptyFopen.getFilenameExpr()) = globalValueNumber(fopen.getFilenameExpr()) +select emptyFopen, + "This call is trying to prevent an exsisting file to be overwritten by $@. An attacker might be able to exploit the race window between the two calls.", + fopen, "another call" diff --git a/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected new file mode 100644 index 0000000000..7bf165306e --- /dev/null +++ b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected @@ -0,0 +1 @@ +| test.c:4:13:4:17 | call to fopen | This call is trying to prevent an exsisting file to be overwritten by $@. An attacker might be able to exploit the race window between the two calls. | test.c:11:9:11:13 | call to fopen | another call | diff --git a/c/cert/test/rules/FIO45-C/test.c b/c/cert/test/rules/FIO45-C/test.c new file mode 100644 index 0000000000..bbe8ad7977 --- /dev/null +++ b/c/cert/test/rules/FIO45-C/test.c @@ -0,0 +1,85 @@ +#include + +void f1(const char *file) { + FILE *f = fopen(file, "r"); // NON_COMPLIANT + if (NULL != f) { + /* File exists, handle error */ + } else { + if (fclose(f) == EOF) { + /* Handle error */ + } + f = fopen(file, "w"); + if (NULL == f) { + /* Handle error */ + } + + /* Write to file */ + if (fclose(f) == EOF) { + /* Handle error */ + } + } +} + +void f2(const char *file) { + FILE *f = fopen(file, "wx"); // COMPLIANT + if (NULL == f) { + /* Handle error */ + } + /* Write to file */ + if (fclose(f) == EOF) { + /* Handle error */ + } +} + +#include +#include + +void f3(const char *file) { + int fd = open(file, O_CREAT | O_EXCL | O_WRONLY); + if (-1 != fd) { + FILE *f = fdopen(fd, "w"); // COMPLIANT + if (NULL != f) { + /* Write to file */ + + if (fclose(f) == EOF) { + /* Handle error */ + } + } else { + if (close(fd) == -1) { + /* Handle error */ + } + } + } +} + +#include + +int f4(char *filename, int flags) { + struct stat lstat_info; + struct stat fstat_info; + int f; + + if (lstat(filename, &lstat_info) == -1) { + /* File does not exist, handle error */ + } + + if (!S_ISREG(lstat_info.st_mode)) { + /* File is not a regular file, handle error */ + } + + if ((f = open(filename, flags)) == -1) { // COMPLIANT + /* File has disappeared, handle error */ + } + + if (fstat(f, &fstat_info) == -1) { + /* Handle error */ + } + + if (lstat_info.st_ino != fstat_info.st_ino || + lstat_info.st_dev != fstat_info.st_dev) { + /* Open file is not the expected regular file, handle error */ + } + + /* f is the expected regular open file */ + return f; +} diff --git a/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll b/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll index 661485b3d3..8da4b8cc3f 100644 --- a/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll +++ b/cpp/common/src/codingstandards/cpp/standardlibrary/FileAccess.qll @@ -56,22 +56,43 @@ class Wint_t extends Type { } /** - * A function call that opens a file + * A standard function call that opens a file */ class FOpenCall extends FunctionCall { - FOpenCall() { this.getTarget().hasGlobalName(["fopen", "fopen_s", "freopen", "freopen_s"]) } + FOpenCall() { + this.getTarget().hasGlobalName(["fopen", "fdopen", "fopen_s", "freopen", "freopen_s", "open"]) + } /** The expression corresponding to the accessed file */ - Expr getFilenameExpr() { result = this.getArgument(getNumberOfArguments() - 2) } + Expr getFilenameExpr() { + this.getTarget().hasGlobalName("open") and result = this.getArgument(0) + or + result = this.getArgument(getNumberOfArguments() - 2) + } - Expr getMode() { result = this.getArgument(getNumberOfArguments() - 1) } + Expr getMode() { + this.getTarget().hasGlobalName("open") and result = this.getArgument(1) + or + result = this.getArgument(getNumberOfArguments() - 1) + } // make predicate predicate isReadMode() { this.getMode().getValue() = ["r", "r+", "w+", "a+"] } predicate isWriteMode() { this.getMode().getValue() = ["w", "a", "r+", "w+", "a+"] } - predicate isReadOnlyMode() { this.isReadMode() and not this.isWriteMode() } + predicate isReadOnlyMode() { + this.isReadMode() and not this.isWriteMode() + or + exists(MacroInvocation mi | + mi.getMacroName() = "O_RDONLY" and + ( + this.getMode() = mi.getExpr() + or + this.getMode().(BitwiseOrExpr).getAnOperand*() = mi.getExpr() + ) + ) + } predicate isReadWriteMode() { this.isReadMode() and this.isWriteMode() } } diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index ac1f49cd3a..ab1572603f 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -9,7 +9,7 @@ "description": "Avoid TOCTOU race conditions accessing files", "kind": "problem", "name": "Avoid TOCTOU race conditions while accessing files", - "precision": "very-high", + "precision": "high", "severity": "error", "short_name": "ToctouRaceConditionsWhileAccessingFiles", "tags": [ @@ -20,7 +20,7 @@ ], "title": "Avoid TOCTOU race conditions while accessing files", "implementation_scope": { - "description": "None" + "description": "The query is limited to one specific class of TOCTOU race conditions." } }, "FIO47-C": { From 0b5ec101572041b753d3ca64fc30a5e8bd1ded50 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 9 Aug 2022 11:23:10 +0200 Subject: [PATCH 03/31] Rule FIO45-C --- ...ernatelyIOFromAStreamWithoutPositioning.ql | 3 +- .../CloseFilesWhenTheyAreNoLongerNeeded.ql | 2 +- ...touRaceConditionsWhileAccessingFiles.qlref | 1 + .../codingstandards/cpp/exclusions/c/IO4.qll | 42 +++++++++++++++++++ .../cpp/exclusions/c/RuleMetadata.qll | 3 ++ 5 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.qlref create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll diff --git a/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql b/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql index bb1cef3259..04f15a4857 100644 --- a/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql +++ b/c/cert/src/rules/FIO39-C/DoNotAlternatelyIOFromAStreamWithoutPositioning.ql @@ -1,7 +1,8 @@ /** * @id c/cert/do-not-alternately-io-from-a-stream-without-positioning * @name FIO39-C: Do not alternately input and output from a stream without an intervening flush or positioning call - * @description + * @description Do not alternate input and output operations on a file without an intervening flush + * or positioning call. * @kind problem * @precision very-high * @problem.severity error diff --git a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql index 28f7d6eb58..2acd27d08b 100644 --- a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql +++ b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql @@ -1,7 +1,7 @@ /** * @id c/cert/close-files-when-they-are-no-longer-needed * @name FIO42-C: Close files when they are no longer needed - * @description + * @description Close files when they are no longer needed * @kind problem * @precision very-high * @problem.severity error diff --git a/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.qlref b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.qlref new file mode 100644 index 0000000000..cd31c49372 --- /dev/null +++ b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.qlref @@ -0,0 +1 @@ +rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll new file mode 100644 index 0000000000..cbc27bec99 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll @@ -0,0 +1,42 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype IO4Query = + TToctouRaceConditionsWhileAccessingFilesQuery() or + TUseValidFormatStringsQuery() + +predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { + query = + // `Query` instance for the `toctouRaceConditionsWhileAccessingFiles` query + IO4Package::toctouRaceConditionsWhileAccessingFilesQuery() and + queryId = + // `@id` for the `toctouRaceConditionsWhileAccessingFiles` query + "c/cert/toctou-race-conditions-while-accessing-files" and + ruleId = "FIO45-C" + or + query = + // `Query` instance for the `useValidFormatStrings` query + IO4Package::useValidFormatStringsQuery() and + queryId = + // `@id` for the `useValidFormatStrings` query + "c/cert/use-valid-format-strings" and + ruleId = "FIO47-C" +} + +module IO4Package { + Query toctouRaceConditionsWhileAccessingFilesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `toctouRaceConditionsWhileAccessingFiles` query + TQueryC(TIO4PackageQuery(TToctouRaceConditionsWhileAccessingFilesQuery())) + } + + Query useValidFormatStringsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useValidFormatStrings` query + TQueryC(TIO4PackageQuery(TUseValidFormatStringsQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index d4813a7e58..e2c0f9e0e6 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -8,6 +8,7 @@ import Concurrency2 import IO1 import IO2 import IO3 +import IO4 import Misc import Pointers1 import Preprocessor1 @@ -30,6 +31,7 @@ newtype TCQuery = TIO1PackageQuery(IO1Query q) or TIO2PackageQuery(IO2Query q) or TIO3PackageQuery(IO3Query q) or + TIO4PackageQuery(IO4Query q) or TMiscPackageQuery(MiscQuery q) or TPointers1PackageQuery(Pointers1Query q) or TPreprocessor1PackageQuery(Preprocessor1Query q) or @@ -52,6 +54,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) { isIO1QueryMetadata(query, queryId, ruleId) or isIO2QueryMetadata(query, queryId, ruleId) or isIO3QueryMetadata(query, queryId, ruleId) or + isIO4QueryMetadata(query, queryId, ruleId) or isMiscQueryMetadata(query, queryId, ruleId) or isPointers1QueryMetadata(query, queryId, ruleId) or isPreprocessor1QueryMetadata(query, queryId, ruleId) or From b992077511f09c7116580d903489a64c5b2a84ab Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 10 Aug 2022 03:31:26 +0200 Subject: [PATCH 04/31] Rule FIO47-C --- .../UseIntArgumentForWidthAndPrecision.md | 152 +++++++ .../UseIntArgumentForWidthAndPrecision.ql | 26 ++ .../rules/FIO47-C/UseValidSpecifierFlags.md | 152 +++++++ .../rules/FIO47-C/UseValidSpecifierFlags.ql | 32 ++ .../rules/FIO47-C/UseValidSpecifierLength.md | 152 +++++++ .../rules/FIO47-C/UseValidSpecifierLength.ql | 34 ++ .../src/rules/FIO47-C/UseValidSpecifiers.md | 152 +++++++ .../src/rules/FIO47-C/UseValidSpecifiers.ql | 23 + .../FIO47-C/WrongNumberOfFormatArguments.md | 152 +++++++ .../FIO47-C/WrongNumberOfFormatArguments.ql | 26 ++ .../rules/FIO47-C/WrongTypeFormatArguments.md | 152 +++++++ .../rules/FIO47-C/WrongTypeFormatArguments.ql | 177 ++++++++ c/cert/src/rules/FIO47-C/check.svg | 1 + c/cert/src/rules/FIO47-C/error.svg | 1 + ...seIntArgumentForWidthAndPrecision.expected | 1 + .../UseIntArgumentForWidthAndPrecision.qlref | 1 + .../FIO47-C/UseValidFormatStrings.expected | 1 + .../rules/FIO47-C/UseValidFormatStrings.qlref | 1 + .../FIO47-C/UseValidSpecifierFlags.expected | 1 + .../FIO47-C/UseValidSpecifierFlags.qlref | 1 + .../FIO47-C/UseValidSpecifierLength.expected | 1 + .../FIO47-C/UseValidSpecifierLength.qlref | 1 + .../rules/FIO47-C/UseValidSpecifiers.expected | 1 + .../rules/FIO47-C/UseValidSpecifiers.qlref | 1 + .../WrongNumberOfFormatArguments.expected | 1 + .../WrongNumberOfFormatArguments.qlref | 1 + .../FIO47-C/WrongTypeFormatArguments.expected | 1 + .../FIO47-C/WrongTypeFormatArguments.qlref | 1 + c/cert/test/rules/FIO47-C/test.c | 394 ++++++++++++++++++ .../codingstandards/cpp/exclusions/c/IO4.qll | 96 ++++- rule_packages/c/IO4.json | 64 ++- 31 files changed, 1790 insertions(+), 10 deletions(-) create mode 100644 c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md create mode 100644 c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql create mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md create mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql create mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md create mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql create mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifiers.md create mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql create mode 100644 c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md create mode 100644 c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql create mode 100644 c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md create mode 100644 c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql create mode 100644 c/cert/src/rules/FIO47-C/check.svg create mode 100644 c/cert/src/rules/FIO47-C/error.svg create mode 100644 c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected create mode 100644 c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref create mode 100644 c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected create mode 100644 c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref create mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected create mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref create mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected create mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref create mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected create mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifiers.qlref create mode 100644 c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected create mode 100644 c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.qlref create mode 100644 c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected create mode 100644 c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.qlref create mode 100644 c/cert/test/rules/FIO47-C/test.c diff --git a/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md b/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md new file mode 100644 index 0000000000..06a6bfd6ae --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md @@ -0,0 +1,152 @@ +# FIO47-C: Use `int` arguments for `width` and `precision` + +This query implements the CERT-C rule FIO47-C: + +> Use valid format strings + + +## Description + +The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies + +> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. + + +Each *conversion specification* is introduced by the `%` character followed (in order) by + +* Zero or more *flags* (in any order), which modify the meaning of the conversion specification +* An optional minimum field *width* +* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier +* An optional *length modifier* that specifies the size of the argument +* A *conversion specifier character* that indicates the type of conversion to be applied +Common mistakes in creating format strings include +* Providing an incorrect number of arguments for the format string +* Using invalid conversion specifiers +* Using a flag character that is incompatible with the conversion specifier +* Using a length modifier that is incompatible with the conversion specifier +* Mismatching the argument type and conversion specifier +* Using an argument of type other than `int` for *width* or *precision* +The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). + +Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. + +Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) + +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension + + +The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. + +Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. + +Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) + +## Noncompliant Code Example + +Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %s): %d\n", error_type, error_msg); + /* ... */ +} +``` + +## Compliant Solution + +This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %d): %s\n", error_type, error_msg); + + /* ... */ +} +``` + +## Risk Assessment + +Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). + +
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-686 and FIO47-C** + +Intersection( EXP37-C, FIO47-C) = + +* Invalid argument types passed to format I/O function +EXP37-C – FIO47-C = +* Invalid argument types passed to non-format I/O function +FIO47-C – EXP37-C = +* Invalid format string, but correctly matches arguments in number and type +Intersection( CWE-686, FIO47-C) = +* Use of format strings that do not match the type of arguments +CWE-686 – FIO47-C = +* Incorrect argument type in functions outside of the printf() family. +FIO47-C – CWE-686 = +* Invalid format strings that still match their arguments in type +**CWE-685 and FIO47-C** + +Intersection( CWE-685, FIO47-C) = + +* Use of format strings that do not match the number of arguments +CWE-685 – FIO47-C = +* Incorrect argument number in functions outside of the printf() family. +FIO47-C – CWE-685 = +* Invalid format strings that still match their arguments in number +**CWE-134 and FIO47-C** + +Intersection( FIO30-C, FIO47-C) = + +* Use of untrusted and ill-specified format string +FIO30-C – FIO47-C = +* Use of untrusted, but well-defined format string +FIO47-C – FIO30-C = +* Use of Ill-defined, but trusted format string +FIO47-C = Union(CWE-134, list) where list = +* Using a trusted but invalid format string + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql b/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql new file mode 100644 index 0000000000..23cc91e1f4 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql @@ -0,0 +1,26 @@ +/** + * @id c/cert/use-int-argument-for-width-and-precision + * @name FIO47-C: Use `int` arguments for `width` and `precision` + * @description Use `int` arguments for `width` and `precision` + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio47-c + * correctness + * security + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +from FormatLiteral fl, Expr expr, string element +where + not isExcluded(expr, IO4Package::useIntArgumentForWidthAndPrecisionQuery()) and + ( + element = "minimum field width" and expr = fl.getUse().getMinFieldWidthArgument(_) + or + element = "precision" and expr = fl.getUse().getPrecisionArgument(_) + ) and + not expr.getType() instanceof IntType +select expr, "The coversion specification argument for '" + element + "' must be of type `int`." diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md b/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md new file mode 100644 index 0000000000..1df2c220b7 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md @@ -0,0 +1,152 @@ +# FIO47-C: Use valid specifier flags + +This query implements the CERT-C rule FIO47-C: + +> Use valid format strings + + +## Description + +The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies + +> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. + + +Each *conversion specification* is introduced by the `%` character followed (in order) by + +* Zero or more *flags* (in any order), which modify the meaning of the conversion specification +* An optional minimum field *width* +* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier +* An optional *length modifier* that specifies the size of the argument +* A *conversion specifier character* that indicates the type of conversion to be applied +Common mistakes in creating format strings include +* Providing an incorrect number of arguments for the format string +* Using invalid conversion specifiers +* Using a flag character that is incompatible with the conversion specifier +* Using a length modifier that is incompatible with the conversion specifier +* Mismatching the argument type and conversion specifier +* Using an argument of type other than `int` for *width* or *precision* +The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). + +Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. + +Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) + +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension + + +The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. + +Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. + +Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) + +## Noncompliant Code Example + +Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %s): %d\n", error_type, error_msg); + /* ... */ +} +``` + +## Compliant Solution + +This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %d): %s\n", error_type, error_msg); + + /* ... */ +} +``` + +## Risk Assessment + +Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). + +
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-686 and FIO47-C** + +Intersection( EXP37-C, FIO47-C) = + +* Invalid argument types passed to format I/O function +EXP37-C – FIO47-C = +* Invalid argument types passed to non-format I/O function +FIO47-C – EXP37-C = +* Invalid format string, but correctly matches arguments in number and type +Intersection( CWE-686, FIO47-C) = +* Use of format strings that do not match the type of arguments +CWE-686 – FIO47-C = +* Incorrect argument type in functions outside of the printf() family. +FIO47-C – CWE-686 = +* Invalid format strings that still match their arguments in type +**CWE-685 and FIO47-C** + +Intersection( CWE-685, FIO47-C) = + +* Use of format strings that do not match the number of arguments +CWE-685 – FIO47-C = +* Incorrect argument number in functions outside of the printf() family. +FIO47-C – CWE-685 = +* Invalid format strings that still match their arguments in number +**CWE-134 and FIO47-C** + +Intersection( FIO30-C, FIO47-C) = + +* Use of untrusted and ill-specified format string +FIO30-C – FIO47-C = +* Use of untrusted, but well-defined format string +FIO47-C – FIO30-C = +* Use of Ill-defined, but trusted format string +FIO47-C = Union(CWE-134, list) where list = +* Using a trusted but invalid format string + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql new file mode 100644 index 0000000000..5a85a70a94 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql @@ -0,0 +1,32 @@ +/** + * @id c/cert/use-valid-specifier-flags + * @name FIO47-C: Use valid specifier flags + * @description Use valid specifier flags + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio47-c + * correctness + * security + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +string getInvalidFlag(string specChar) { + specChar = ["d", "i", "u"] and result = ["#"] + or + specChar = ["o", "x", "X", "e", "E"] and result = ["'"] + or + specChar = ["c", "s", "p", "n", "C", "S", "%"] and result = ["'", "#", "0"] +} + +from FormatLiteral fl, string specChar, string flags +where + not isExcluded(fl, IO4Package::useValidSpecifierFlagsQuery()) and + flags = fl.getFlags(_) and + specChar = fl.getConversionChar(_) and + flags.matches("%" + getInvalidFlag(specChar) + "%") +select fl, + "The conversion specifier '" + specChar + "' is not compatible with flags '" + flags + "'." diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md b/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md new file mode 100644 index 0000000000..b97c25b273 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md @@ -0,0 +1,152 @@ +# FIO47-C: Use valid specifier length + +This query implements the CERT-C rule FIO47-C: + +> Use valid format strings + + +## Description + +The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies + +> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. + + +Each *conversion specification* is introduced by the `%` character followed (in order) by + +* Zero or more *flags* (in any order), which modify the meaning of the conversion specification +* An optional minimum field *width* +* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier +* An optional *length modifier* that specifies the size of the argument +* A *conversion specifier character* that indicates the type of conversion to be applied +Common mistakes in creating format strings include +* Providing an incorrect number of arguments for the format string +* Using invalid conversion specifiers +* Using a flag character that is incompatible with the conversion specifier +* Using a length modifier that is incompatible with the conversion specifier +* Mismatching the argument type and conversion specifier +* Using an argument of type other than `int` for *width* or *precision* +The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). + +Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. + +Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) + +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension + + +The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. + +Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. + +Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) + +## Noncompliant Code Example + +Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %s): %d\n", error_type, error_msg); + /* ... */ +} +``` + +## Compliant Solution + +This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %d): %s\n", error_type, error_msg); + + /* ... */ +} +``` + +## Risk Assessment + +Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). + +
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-686 and FIO47-C** + +Intersection( EXP37-C, FIO47-C) = + +* Invalid argument types passed to format I/O function +EXP37-C – FIO47-C = +* Invalid argument types passed to non-format I/O function +FIO47-C – EXP37-C = +* Invalid format string, but correctly matches arguments in number and type +Intersection( CWE-686, FIO47-C) = +* Use of format strings that do not match the type of arguments +CWE-686 – FIO47-C = +* Incorrect argument type in functions outside of the printf() family. +FIO47-C – CWE-686 = +* Invalid format strings that still match their arguments in type +**CWE-685 and FIO47-C** + +Intersection( CWE-685, FIO47-C) = + +* Use of format strings that do not match the number of arguments +CWE-685 – FIO47-C = +* Incorrect argument number in functions outside of the printf() family. +FIO47-C – CWE-685 = +* Invalid format strings that still match their arguments in number +**CWE-134 and FIO47-C** + +Intersection( FIO30-C, FIO47-C) = + +* Use of untrusted and ill-specified format string +FIO30-C – FIO47-C = +* Use of untrusted, but well-defined format string +FIO47-C – FIO30-C = +* Use of Ill-defined, but trusted format string +FIO47-C = Union(CWE-134, list) where list = +* Using a trusted but invalid format string + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql new file mode 100644 index 0000000000..f5b8245812 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql @@ -0,0 +1,34 @@ +/** + * @id c/cert/use-valid-specifier-length + * @name FIO47-C: Use valid specifier length + * @description Use valid specifier length + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio47-c + * correctness + * security + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +string getInvalidLength(string specChar) { + specChar = ["d", "i", "o", "u", "n"] and result = ["L"] + or + specChar = ["f", "F", "e", "E", "g", "G", "a", "A"] and result = ["h", "hh", "j", "z", "t"] + or + specChar = ["c", "s"] and result = ["h", "hh", "ll", "j", "z", "t", "L"] + or + specChar = ["p", "C", "S", "%"] and result = ["h", "hh", "l", "ll", "j", "z", "t", "L"] +} + +from FormatLiteral fl, string specChar, string length +where + not isExcluded(fl, IO4Package::useValidSpecifierLengthQuery()) and + length = fl.getLength(_) and + specChar = fl.getConversionChar(_) and + length.matches("%" + getInvalidLength(specChar) + "%") +select fl, + "The conversion specifier '" + specChar + "' is not compatible with length '" + length + "'." diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md new file mode 100644 index 0000000000..46df2d9465 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md @@ -0,0 +1,152 @@ +# FIO47-C: Use valid format strings + +This query implements the CERT-C rule FIO47-C: + +> Use valid format strings + + +## Description + +The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies + +> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. + + +Each *conversion specification* is introduced by the `%` character followed (in order) by + +* Zero or more *flags* (in any order), which modify the meaning of the conversion specification +* An optional minimum field *width* +* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier +* An optional *length modifier* that specifies the size of the argument +* A *conversion specifier character* that indicates the type of conversion to be applied +Common mistakes in creating format strings include +* Providing an incorrect number of arguments for the format string +* Using invalid conversion specifiers +* Using a flag character that is incompatible with the conversion specifier +* Using a length modifier that is incompatible with the conversion specifier +* Mismatching the argument type and conversion specifier +* Using an argument of type other than `int` for *width* or *precision* +The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). + +Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. + +Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) + +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension + + +The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. + +Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. + +Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) + +## Noncompliant Code Example + +Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %s): %d\n", error_type, error_msg); + /* ... */ +} +``` + +## Compliant Solution + +This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %d): %s\n", error_type, error_msg); + + /* ... */ +} +``` + +## Risk Assessment + +Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). + +
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-686 and FIO47-C** + +Intersection( EXP37-C, FIO47-C) = + +* Invalid argument types passed to format I/O function +EXP37-C – FIO47-C = +* Invalid argument types passed to non-format I/O function +FIO47-C – EXP37-C = +* Invalid format string, but correctly matches arguments in number and type +Intersection( CWE-686, FIO47-C) = +* Use of format strings that do not match the type of arguments +CWE-686 – FIO47-C = +* Incorrect argument type in functions outside of the printf() family. +FIO47-C – CWE-686 = +* Invalid format strings that still match their arguments in type +**CWE-685 and FIO47-C** + +Intersection( CWE-685, FIO47-C) = + +* Use of format strings that do not match the number of arguments +CWE-685 – FIO47-C = +* Incorrect argument number in functions outside of the printf() family. +FIO47-C – CWE-685 = +* Invalid format strings that still match their arguments in number +**CWE-134 and FIO47-C** + +Intersection( FIO30-C, FIO47-C) = + +* Use of untrusted and ill-specified format string +FIO30-C – FIO47-C = +* Use of untrusted, but well-defined format string +FIO47-C – FIO30-C = +* Use of Ill-defined, but trusted format string +FIO47-C = Union(CWE-134, list) where list = +* Using a trusted but invalid format string + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql new file mode 100644 index 0000000000..ad98f3d7a0 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql @@ -0,0 +1,23 @@ +/** + * @id c/cert/use-valid-specifiers + * @name FIO47-C: Use valid format strings + * @description Use valid conversion specifier + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio47-c + * correctness + * security + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +class InvalidSpecifier extends FormatLiteral { + InvalidSpecifier() { not this.specsAreKnown() } +} + +from InvalidSpecifier x +where not isExcluded(x, IO4Package::useValidSpecifiersQuery()) +select x, "The conversion specifier '" + x + "' is not valid." diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md new file mode 100644 index 0000000000..3025b0c631 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md @@ -0,0 +1,152 @@ +# FIO47-C: Use correct number fo arguments + +This query implements the CERT-C rule FIO47-C: + +> Use valid format strings + + +## Description + +The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies + +> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. + + +Each *conversion specification* is introduced by the `%` character followed (in order) by + +* Zero or more *flags* (in any order), which modify the meaning of the conversion specification +* An optional minimum field *width* +* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier +* An optional *length modifier* that specifies the size of the argument +* A *conversion specifier character* that indicates the type of conversion to be applied +Common mistakes in creating format strings include +* Providing an incorrect number of arguments for the format string +* Using invalid conversion specifiers +* Using a flag character that is incompatible with the conversion specifier +* Using a length modifier that is incompatible with the conversion specifier +* Mismatching the argument type and conversion specifier +* Using an argument of type other than `int` for *width* or *precision* +The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). + +Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. + +Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) + +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension + + +The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. + +Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. + +Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) + +## Noncompliant Code Example + +Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %s): %d\n", error_type, error_msg); + /* ... */ +} +``` + +## Compliant Solution + +This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %d): %s\n", error_type, error_msg); + + /* ... */ +} +``` + +## Risk Assessment + +Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). + +
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-686 and FIO47-C** + +Intersection( EXP37-C, FIO47-C) = + +* Invalid argument types passed to format I/O function +EXP37-C – FIO47-C = +* Invalid argument types passed to non-format I/O function +FIO47-C – EXP37-C = +* Invalid format string, but correctly matches arguments in number and type +Intersection( CWE-686, FIO47-C) = +* Use of format strings that do not match the type of arguments +CWE-686 – FIO47-C = +* Incorrect argument type in functions outside of the printf() family. +FIO47-C – CWE-686 = +* Invalid format strings that still match their arguments in type +**CWE-685 and FIO47-C** + +Intersection( CWE-685, FIO47-C) = + +* Use of format strings that do not match the number of arguments +CWE-685 – FIO47-C = +* Incorrect argument number in functions outside of the printf() family. +FIO47-C – CWE-685 = +* Invalid format strings that still match their arguments in number +**CWE-134 and FIO47-C** + +Intersection( FIO30-C, FIO47-C) = + +* Use of untrusted and ill-specified format string +FIO30-C – FIO47-C = +* Use of untrusted, but well-defined format string +FIO47-C – FIO30-C = +* Use of Ill-defined, but trusted format string +FIO47-C = Union(CWE-134, list) where list = +* Using a trusted but invalid format string + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql new file mode 100644 index 0000000000..c974949559 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql @@ -0,0 +1,26 @@ +/** + * @id c/cert/wrong-number-of-format-arguments + * @name FIO47-C: Use correct number fo arguments + * @description Use correct number of format arguments + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio47-c + * correctness + * security + * external/cert/obligation/rule + */ + +// Copy of Standard Library's query WrongNumberOfFormatArguments.ql +import cpp +import codingstandards.c.cert + +from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given +where + not isExcluded(ffc, IO4Package::wrongNumberOfFormatArgumentsQuery()) and + ffc = fl.getUse() and + expected = fl.getNumArgNeeded() and + given = ffc.getNumFormatArgument() and + expected > given and + fl.specsAreKnown() +select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString() diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md new file mode 100644 index 0000000000..de84d18d6c --- /dev/null +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md @@ -0,0 +1,152 @@ +# FIO47-C: Wrong type format arguments + +This query implements the CERT-C rule FIO47-C: + +> Use valid format strings + + +## Description + +The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies + +> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. + + +Each *conversion specification* is introduced by the `%` character followed (in order) by + +* Zero or more *flags* (in any order), which modify the meaning of the conversion specification +* An optional minimum field *width* +* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier +* An optional *length modifier* that specifies the size of the argument +* A *conversion specifier character* that indicates the type of conversion to be applied +Common mistakes in creating format strings include +* Providing an incorrect number of arguments for the format string +* Using invalid conversion specifiers +* Using a flag character that is incompatible with the conversion specifier +* Using a length modifier that is incompatible with the conversion specifier +* Mismatching the argument type and conversion specifier +* Using an argument of type other than `int` for *width* or *precision* +The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). + +Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. + +Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) + +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension + + +The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. + +Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. + +Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) + +## Noncompliant Code Example + +Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %s): %d\n", error_type, error_msg); + /* ... */ +} +``` + +## Compliant Solution + +This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: + +```cpp +#include + +void func(void) { + const char *error_msg = "Resource not available to user."; + int error_type = 3; + /* ... */ + printf("Error (type %d): %s\n", error_type, error_msg); + + /* ... */ +} +``` + +## Risk Assessment + +Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). + +
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-686 and FIO47-C** + +Intersection( EXP37-C, FIO47-C) = + +* Invalid argument types passed to format I/O function +EXP37-C – FIO47-C = +* Invalid argument types passed to non-format I/O function +FIO47-C – EXP37-C = +* Invalid format string, but correctly matches arguments in number and type +Intersection( CWE-686, FIO47-C) = +* Use of format strings that do not match the type of arguments +CWE-686 – FIO47-C = +* Incorrect argument type in functions outside of the printf() family. +FIO47-C – CWE-686 = +* Invalid format strings that still match their arguments in type +**CWE-685 and FIO47-C** + +Intersection( CWE-685, FIO47-C) = + +* Use of format strings that do not match the number of arguments +CWE-685 – FIO47-C = +* Incorrect argument number in functions outside of the printf() family. +FIO47-C – CWE-685 = +* Invalid format strings that still match their arguments in number +**CWE-134 and FIO47-C** + +Intersection( FIO30-C, FIO47-C) = + +* Use of untrusted and ill-specified format string +FIO30-C – FIO47-C = +* Use of untrusted, but well-defined format string +FIO47-C – FIO30-C = +* Use of Ill-defined, but trusted format string +FIO47-C = Union(CWE-134, list) where list = +* Using a trusted but invalid format string + +## Bibliography + +
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql new file mode 100644 index 0000000000..338923152b --- /dev/null +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql @@ -0,0 +1,177 @@ +/** + * @id c/cert/wrong-type-format-arguments + * @name FIO47-C: Wrong type format arguments + * @description Wrong type format arguments + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio47-c + * correctness + * security + * external/cert/obligation/rule + */ + +// Copy of Standard Library's query WrongTypeFormatArguments.ql +import cpp +import codingstandards.c.cert + +/** + * Holds if the argument corresponding to the `pos` conversion specifier + * of `ffc` is expected to have type `expected`. + */ +private predicate formattingFunctionCallExpectedType( + FormattingFunctionCall ffc, int pos, Type expected +) { + ffc.getFormat().(FormatLiteral).getConversionType(pos) = expected +} + +/** + * Holds if the argument corresponding to the `pos` conversion specifier + * of `ffc` could alternatively have type `expected`, for example on a different + * platform. + */ +private predicate formattingFunctionCallAlternateType( + FormattingFunctionCall ffc, int pos, Type expected +) { + ffc.getFormat().(FormatLiteral).getConversionTypeAlternate(pos) = expected +} + +/** + * Holds if the argument corresponding to the `pos` conversion specifier + * of `ffc` is `arg` and has type `actual`. + */ +pragma[noopt] +predicate formattingFunctionCallActualType( + FormattingFunctionCall ffc, int pos, Expr arg, Type actual +) { + exists(Expr argConverted | + ffc.getConversionArgument(pos) = arg and + argConverted = arg.getFullyConverted() and + actual = argConverted.getType() + ) +} + +/** + * Holds if the argument corresponding to the `pos` conversion specifier + * of `ffc` is expected to have a width or precision argument of type + * `expected` and the corresponding argument `arg` has type `actual`. + */ +pragma[noopt] +predicate formatOtherArgType( + FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual +) { + exists(Expr argConverted | + (arg = ffc.getMinFieldWidthArgument(pos) or arg = ffc.getPrecisionArgument(pos)) and + argConverted = arg.getFullyConverted() and + actual = argConverted.getType() and + exists(IntType it | it instanceof IntType and it.isImplicitlySigned() and expected = it) + ) +} + +/** + * A type that may be expected by a printf format parameter, or that may + * be pointed to by such a type (e.g. `wchar_t`, from `wchar_t *`). + */ +class ExpectedType extends Type { + ExpectedType() { + exists(Type t | + ( + formattingFunctionCallExpectedType(_, _, t) or + formattingFunctionCallAlternateType(_, _, t) or + formatOtherArgType(_, _, t, _, _) + ) and + this = t.getUnspecifiedType() + ) + } +} + +/** + * Holds if it is safe to display a value of type `actual` when `printf` + * expects a value of type `expected`. + * + * Note that variadic arguments undergo default argument promotions before + * they reach `printf`, notably `bool`, `char`, `short` and `enum` types + * are promoted to `int` (or `unsigned int`, as appropriate) and `float`s + * are converted to `double`. + */ +predicate trivialConversion(ExpectedType expected, Type actual) { + exists(Type exp, Type act | + ( + formattingFunctionCallExpectedType(_, _, exp) or + formattingFunctionCallAlternateType(_, _, exp) + ) and + formattingFunctionCallActualType(_, _, _, act) and + expected = exp.getUnspecifiedType() and + actual = act.getUnspecifiedType() + ) and + ( + // allow a pointer type to be displayed with `%p` + expected instanceof VoidPointerType and actual instanceof PointerType + or + // allow a function pointer type to be displayed with `%p` + expected instanceof VoidPointerType and + actual instanceof FunctionPointerType and + expected.getSize() = actual.getSize() + or + // allow an `enum` type to be displayed with `%i`, `%c` etc + expected instanceof IntegralType and actual instanceof Enum + or + // allow any `char *` type to be displayed with `%s` + expected instanceof CharPointerType and actual instanceof CharPointerType + or + // allow `wchar_t *`, or any pointer to an integral type of the same size, to be displayed + // with `%ws` + expected.(PointerType).getBaseType().hasName("wchar_t") and + exists(Wchar_t t | + actual.getUnspecifiedType().(PointerType).getBaseType().(IntegralType).getSize() = t.getSize() + ) + or + // allow an `int` (or anything promoted to `int`) to be displayed with `%c` + expected instanceof CharType and actual instanceof IntType + or + // allow an `int` (or anything promoted to `int`) to be displayed with `%wc` + expected instanceof Wchar_t and actual instanceof IntType + or + expected instanceof UnsignedCharType and actual instanceof IntType + or + // allow any integral type of the same size + // (this permits signedness changes) + expected.(IntegralType).getSize() = actual.(IntegralType).getSize() + or + // allow a pointer to any integral type of the same size + // (this permits signedness changes) + expected.(PointerType).getBaseType().(IntegralType).getSize() = + actual.(PointerType).getBaseType().(IntegralType).getSize() + or + expected = actual + ) +} + +/** + * Gets the size of the `int` type. + */ +int sizeof_IntType() { exists(IntType it | result = it.getSize()) } + +from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual +where + not isExcluded(arg, IO4Package::wrongTypeFormatArgumentsQuery()) and + ( + formattingFunctionCallExpectedType(ffc, n, expected) and + formattingFunctionCallActualType(ffc, n, arg, actual) and + not exists(Type anyExpected | + ( + formattingFunctionCallExpectedType(ffc, n, anyExpected) or + formattingFunctionCallAlternateType(ffc, n, anyExpected) + ) and + trivialConversion(anyExpected.getUnspecifiedType(), actual.getUnspecifiedType()) + ) + or + formatOtherArgType(ffc, n, expected, arg, actual) and + not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType() + ) and + not arg.isAffectedByMacro() and + not arg.isFromUninstantiatedTemplate(_) and + not actual.getUnspecifiedType() instanceof ErroneousType +select arg, + "This argument should be of type '" + expected.getName() + "' but is of type '" + + actual.getUnspecifiedType().getName() + "'" diff --git a/c/cert/src/rules/FIO47-C/check.svg b/c/cert/src/rules/FIO47-C/check.svg new file mode 100644 index 0000000000..e9573db5e7 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/c/cert/src/rules/FIO47-C/error.svg b/c/cert/src/rules/FIO47-C/error.svg new file mode 100644 index 0000000000..b897217cb9 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected b/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref b/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref new file mode 100644 index 0000000000..cf4bd0bc3f --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref @@ -0,0 +1 @@ +rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected b/c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref b/c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref new file mode 100644 index 0000000000..7e6481309d --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref @@ -0,0 +1 @@ +rules/FIO47-C/UseValidFormatStrings.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref b/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref new file mode 100644 index 0000000000..cb97d649a8 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref @@ -0,0 +1 @@ +rules/FIO47-C/UseValidSpecifierFlags.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref b/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref new file mode 100644 index 0000000000..9c31c9f49d --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref @@ -0,0 +1 @@ +rules/FIO47-C/UseValidSpecifierLength.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifiers.qlref b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.qlref new file mode 100644 index 0000000000..6b4f93d3f0 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.qlref @@ -0,0 +1 @@ +rules/FIO47-C/UseValidSpecifiers.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.qlref b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.qlref new file mode 100644 index 0000000000..9d40c663a6 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.qlref @@ -0,0 +1 @@ +rules/FIO47-C/WrongNumberOfFormatArguments.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected new file mode 100644 index 0000000000..2ec1a0ac6c --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected @@ -0,0 +1 @@ +No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.qlref b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.qlref new file mode 100644 index 0000000000..970617a49e --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.qlref @@ -0,0 +1 @@ +rules/FIO47-C/WrongTypeFormatArguments.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/test.c b/c/cert/test/rules/FIO47-C/test.c new file mode 100644 index 0000000000..899d9cfb80 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/test.c @@ -0,0 +1,394 @@ +#include + +void f1() { + const char *s = "Hello"; + printf("Strings - padding:\n"); + printf("\t.%10s.\n\t.%-10s.\n\t.%*s.\n", // COMPLIANT + s, s, 10, s); + printf("Strings - truncating:\n"); + printf("\t%.4s\n\t%.*s\n", s, 3, s); // COMPLIANT + + printf("Characters:\t%c %%\n", 65); // COMPLIANT + + printf("Integers\n"); + printf("Decimal:\t%i %d %.6i %i %.0i %+i %i\n", // COMPLIANT + 1, 2, 3, 0, 0, 4, -4); + printf("Hexadecimal:\t%x %x %X %#x\n", 5, 10, 10, 6); // COMPLIANT + printf("Octal:\t\t%o %#o %#o\n", 10, 10, 4); // COMPLIANT + + printf("Floating point\n"); + printf("Rounding:\t%f %.0f %.32f\n", 1.5, 1.5, 1.3); // COMPLIANT + printf("Padding:\t%05.2f %.2f %5.2f\n", 1.5, 1.5, 1.5); // COMPLIANT + printf("Scientific:\t%E %e\n", 1.5, 1.5); // COMPLIANT + printf("Hexadecimal:\t%a %A\n", 1.5, 1.5); // COMPLIANT +} + +void test_invalid_specifier() { + printf("%d", 42); // COMPLIANT + printf("%b", 42); // NON_COMPLIANT +} +void test_incompatible_flag() { + printf("%'-d", 42); // COMPLIANT + printf("%'-#d", 42); // NON_COMPLIANT + + printf("%'d", 42); // COMPLIANT + printf("%-d", 42); // COMPLIANT + printf("%+d", 42); // COMPLIANT + printf("% d", 42); // COMPLIANT + printf("%#d", 42); // NON_COMPLIANT + printf("%0d", 42); // COMPLIANT + + printf("%'i", 42); // COMPLIANT + printf("%-i", 42); // COMPLIANT + printf("%+i", 42); // COMPLIANT + printf("% i", 42); // COMPLIANT + printf("%#i", 42); // NON_COMPLIANT + printf("%0i", 42); // COMPLIANT + + printf("%'o", 42); // NON_COMPLIANT + printf("%-o", 42); // COMPLIANT + printf("%+o", 42); // COMPLIANT + printf("% o", 42); // COMPLIANT + printf("%#o", 42); // COMPLIANT + printf("%0o", 42); // COMPLIANT + + printf("%'u", 42); // COMPLIANT + printf("%-u", 42); // COMPLIANT + printf("%+u", 42); // COMPLIANT + printf("% u", 42); // COMPLIANT + printf("%#u", 42); // NON_COMPLIANT + printf("%0u", 42); // COMPLIANT + + printf("%'x", 42); // NON_COMPLIANT + printf("%-x", 42); // COMPLIANT + printf("%+x", 42); // COMPLIANT + printf("% x", 42); // COMPLIANT + printf("%#x", 42); // COMPLIANT + printf("%0x", 42); // COMPLIANT + + printf("%'X", 42); // NON_COMPLIANT + printf("%-X", 42); // COMPLIANT + printf("%+X", 42); // COMPLIANT + printf("% X", 42); // COMPLIANT + printf("%#X", 42); // COMPLIANT + printf("%0X", 42); // COMPLIANT + + printf("%'f", 42); // COMPLIANT + printf("%-f", 42); // COMPLIANT + printf("%+f", 42); // COMPLIANT + printf("% f", 42); // COMPLIANT + printf("%#f", 42); // COMPLIANT + printf("%0f", 42); // COMPLIANT + + printf("%'F", 42); // COMPLIANT + printf("%-F", 42); // COMPLIANT + printf("%+F", 42); // COMPLIANT + printf("% F", 42); // COMPLIANT + printf("%#F", 42); // COMPLIANT + printf("%0F", 42); // COMPLIANT + + printf("%'e", 42); // NON_COMPLIANT + printf("%-e", 42); // COMPLIANT + printf("%+e", 42); // COMPLIANT + printf("% e", 42); // COMPLIANT + printf("%#e", 42); // COMPLIANT + printf("%0e", 42); // COMPLIANT + + printf("%'E", 42); // NON_COMPLIANT + printf("%-E", 42); // COMPLIANT + printf("%+E", 42); // COMPLIANT + printf("% E", 42); // COMPLIANT + printf("%#E", 42); // COMPLIANT + printf("%0E", 42); // COMPLIANT + + printf("%'g", 42); // COMPLIANT + printf("%-g", 42); // COMPLIANT + printf("%+g", 42); // COMPLIANT + printf("% g", 42); // COMPLIANT + printf("%#g", 42); // COMPLIANT + printf("%0g", 42); // COMPLIANT + + printf("%'G", 42); // COMPLIANT + printf("%-G", 42); // COMPLIANT + printf("%+G", 42); // COMPLIANT + printf("% G", 42); // COMPLIANT + printf("%#G", 42); // COMPLIANT + printf("%0G", 42); // COMPLIANT + + printf("%'a", 42); // COMPLIANT + printf("%-a", 42); // COMPLIANT + printf("%+a", 42); // COMPLIANT + printf("% a", 42); // COMPLIANT + printf("%#a", 42); // COMPLIANT + printf("%0a", 42); // COMPLIANT + + printf("%'A", 42); // COMPLIANT + printf("%-A", 42); // COMPLIANT + printf("%+A", 42); // COMPLIANT + printf("% A", 42); // COMPLIANT + printf("%#A", 42); // COMPLIANT + printf("%0A", 42); // COMPLIANT + + printf("%'c", 42); // NON_COMPLIANT + printf("%-c", 42); // COMPLIANT + printf("%+c", 42); // COMPLIANT + printf("% c", 42); // COMPLIANT + printf("%#c", 42); // NON_COMPLIANT + printf("%0c", 42); // NON_COMPLIANT + + printf("%'s", 42); // NON_COMPLIANT + printf("%-s", 42); // COMPLIANT + printf("%+s", 42); // COMPLIANT + printf("% s", 42); // COMPLIANT + printf("%#s", 42); // NON_COMPLIANT + printf("%0s", 42); // NON_COMPLIANT + + printf("%'p", 42); // NON_COMPLIANT + printf("%-p", 42); // COMPLIANT + printf("%+p", 42); // COMPLIANT + printf("% p", 42); // COMPLIANT + printf("%#p", 42); // NON_COMPLIANT + printf("%0p", 42); // NON_COMPLIANT + + printf("%'n", 42); // NON_COMPLIANT + printf("%-n", 42); // COMPLIANT + printf("%+n", 42); // COMPLIANT + printf("% n", 42); // COMPLIANT + printf("%#n", 42); // NON_COMPLIANT + printf("%0n", 42); // NON_COMPLIANT + + printf("%'C", 42); // NON_COMPLIANT + printf("%-C", 42); // COMPLIANT + printf("%+C", 42); // COMPLIANT + printf("% C", 42); // COMPLIANT + printf("%#C", 42); // NON_COMPLIANT + printf("%0C", 42); // NON_COMPLIANT + + printf("%'S", 42); // NON_COMPLIANT + printf("%-S", 42); // COMPLIANT + printf("%+S", 42); // COMPLIANT + printf("% S", 42); // COMPLIANT + printf("%#S", 42); // NON_COMPLIANT + printf("%0S", 42); // NON_COMPLIANT + + printf("%%", 42); // COMPLIANT + printf("%'%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%-%", 42); // COMPLIANT + printf("%+%", 42); // COMPLIANT + printf("% %", 42); // COMPLIANT + printf("%#%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%0%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] +} +void test_incompatible_length() { + printf("%hd", 42); // COMPLIANT + printf("%hhd", 42); // COMPLIANT + printf("%ld", 42); // COMPLIANT + printf("%lld", 42); // COMPLIANT + printf("%jd", 42); // COMPLIANT + printf("%zd", 42); // COMPLIANT + printf("%td", 42); // COMPLIANT + printf("%Ld", 42); // NON_COMPLIANT + + printf("%hi", 42); // COMPLIANT + printf("%hhi", 42); // COMPLIANT + printf("%li", 42); // COMPLIANT + printf("%lli", 42); // COMPLIANT + printf("%ji", 42); // COMPLIANT + printf("%zi", 42); // COMPLIANT + printf("%ti", 42); // COMPLIANT + printf("%Li", 42); // NON_COMPLIANT + + printf("%ho", 42); // COMPLIANT + printf("%hho", 42); // COMPLIANT + printf("%lo", 42); // COMPLIANT + printf("%llo", 42); // COMPLIANT + printf("%jo", 42); // COMPLIANT + printf("%zo", 42); // COMPLIANT + printf("%to", 42); // COMPLIANT + printf("%Lo", 42); // NON_COMPLIANT + + printf("%hu", 42); // COMPLIANT + printf("%hhu", 42); // COMPLIANT + printf("%lu", 42); // COMPLIANT + printf("%llu", 42); // COMPLIANT + printf("%ju", 42); // COMPLIANT + printf("%zu", 42); // COMPLIANT + printf("%tu", 42); // COMPLIANT + printf("%Lu", 42); // NON_COMPLIANT + + printf("%hx", 42); // NON_COMPLIANT + printf("%hhx", 42); // NON_COMPLIANT + printf("%lx", 42); // COMPLIANT + printf("%llx", 42); // COMPLIANT + printf("%jx", 42); // NON_COMPLIANT + printf("%zx", 42); // NON_COMPLIANT + printf("%tx", 42); // NON_COMPLIANT + printf("%Lx", 42); // NON_COMPLIANT + + printf("%hX", 42); // NON_COMPLIANT + printf("%hhX", 42); // NON_COMPLIANT + printf("%lX", 42); // COMPLIANT + printf("%llX", 42); // COMPLIANT + printf("%jX", 42); // NON_COMPLIANT + printf("%zX", 42); // NON_COMPLIANT + printf("%tX", 42); // NON_COMPLIANT + printf("%LX", 42); // NON_COMPLIANT + + printf("%hf", 42); // NON_COMPLIANT + printf("%hhf", 42); // NON_COMPLIANT + printf("%lf", 42); // COMPLIANT + printf("%llf", 42); // COMPLIANT + printf("%jf", 42); // NON_COMPLIANT + printf("%zf", 42); // NON_COMPLIANT + printf("%tf", 42); // NON_COMPLIANT + printf("%Lf", 42); // COMPLIANT + + printf("%hF", 42); // NON_COMPLIANT + printf("%hhF", 42); // NON_COMPLIANT + printf("%lF", 42); // COMPLIANT + printf("%llF", 42); // COMPLIANT + printf("%jF", 42); // NON_COMPLIANT + printf("%zF", 42); // NON_COMPLIANT + printf("%tF", 42); // NON_COMPLIANT + printf("%LF", 42); // COMPLIANT + + printf("%he", 42); // NON_COMPLIANT + printf("%hhe", 42); // NON_COMPLIANT + printf("%le", 42); // COMPLIANT + printf("%lle", 42); // COMPLIANT + printf("%je", 42); // NON_COMPLIANT + printf("%ze", 42); // NON_COMPLIANT + printf("%te", 42); // NON_COMPLIANT + printf("%Le", 42); // COMPLIANT + + printf("%hE", 42); // NON_COMPLIANT + printf("%hhE", 42); // NON_COMPLIANT + printf("%lE", 42); // COMPLIANT + printf("%llE", 42); // COMPLIANT + printf("%jE", 42); // NON_COMPLIANT + printf("%zE", 42); // NON_COMPLIANT + printf("%tE", 42); // NON_COMPLIANT + printf("%LE", 42); // COMPLIANT + + printf("%hg", 42); // NON_COMPLIANT + printf("%hhg", 42); // NON_COMPLIANT + printf("%lg", 42); // COMPLIANT + printf("%llg", 42); // COMPLIANT + printf("%jg", 42); // NON_COMPLIANT + printf("%zg", 42); // NON_COMPLIANT + printf("%tg", 42); // NON_COMPLIANT + printf("%Lg", 42); // COMPLIANT + + printf("%hG", 42); // NON_COMPLIANT + printf("%hhG", 42); // NON_COMPLIANT + printf("%lG", 42); // COMPLIANT + printf("%llG", 42); // COMPLIANT + printf("%jG", 42); // NON_COMPLIANT + printf("%zG", 42); // NON_COMPLIANT + printf("%tG", 42); // NON_COMPLIANT + printf("%LG", 42); // COMPLIANT + + printf("%ha", 42); // NON_COMPLIANT + printf("%hha", 42); // NON_COMPLIANT + printf("%la", 42); // COMPLIANT + printf("%lla", 42); // COMPLIANT + printf("%ja", 42); // NON_COMPLIANT + printf("%za", 42); // NON_COMPLIANT + printf("%ta", 42); // NON_COMPLIANT + printf("%La", 42); // COMPLIANT + + printf("%hA", 42); // NON_COMPLIANT + printf("%hhA", 42); // NON_COMPLIANT + printf("%lA", 42); // COMPLIANT + printf("%llA", 42); // COMPLIANT + printf("%jA", 42); // NON_COMPLIANT + printf("%zA", 42); // NON_COMPLIANT + printf("%tA", 42); // NON_COMPLIANT + printf("%LA", 42); // COMPLIANT + + printf("%hc", 42); // NON_COMPLIANT + printf("%hhc", 42); // NON_COMPLIANT + printf("%lc", 42); // COMPLIANT + printf("%llc", 42); // NON_COMPLIANT + printf("%jc", 42); // NON_COMPLIANT + printf("%zc", 42); // NON_COMPLIANT + printf("%tc", 42); // NON_COMPLIANT + printf("%Lc", 42); // NON_COMPLIANT + + printf("%hs", 42); // NON_COMPLIANT + printf("%hhs", 42); // NON_COMPLIANT + printf("%ls", 42); // COMPLIANT + printf("%lls", 42); // NON_COMPLIANT + printf("%js", 42); // NON_COMPLIANT + printf("%zs", 42); // NON_COMPLIANT + printf("%ts", 42); // NON_COMPLIANT + printf("%Ls", 42); // NON_COMPLIANT + + printf("%hp", 42); // NON_COMPLIANT + printf("%hhp", 42); // NON_COMPLIANT + printf("%lp", 42); // NON_COMPLIANT + printf("%llp", 42); // NON_COMPLIANT + printf("%jp", 42); // NON_COMPLIANT + printf("%zp", 42); // NON_COMPLIANT + printf("%tp", 42); // NON_COMPLIANT + printf("%Lp", 42); // NON_COMPLIANT + + printf("%hn", 42); // COMPLIANT + printf("%hhn", 42); // COMPLIANT + printf("%ln", 42); // COMPLIANT + printf("%lln", 42); // COMPLIANT + printf("%jn", 42); // COMPLIANT + printf("%zn", 42); // COMPLIANT + printf("%tn", 42); // COMPLIANT + printf("%Ln", 42); // NON_COMPLIANT + + printf("%hC", 42); // NON_COMPLIANT + printf("%hhC", 42); // NON_COMPLIANT + printf("%lC", 42); // NON_COMPLIANT + printf("%llC", 42); // NON_COMPLIANT + printf("%jC", 42); // NON_COMPLIANT + printf("%zC", 42); // NON_COMPLIANT + printf("%tC", 42); // NON_COMPLIANT + printf("%LC", 42); // NON_COMPLIANT + + printf("%hS", 42); // NON_COMPLIANT + printf("%hhS", 42); // NON_COMPLIANT + printf("%lS", 42); // NON_COMPLIANT + printf("%llS", 42); // NON_COMPLIANT + printf("%jS", 42); // NON_COMPLIANT + printf("%zS", 42); // NON_COMPLIANT + printf("%tS", 42); // NON_COMPLIANT + printf("%LS", 42); // NON_COMPLIANT + + printf("%h%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%hh%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%l%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%ll%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%j%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%z%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%t%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%L%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] +} +void test_wrong_width_type() { + printf("%*d", 2, 42); // COMPLIANT + printf("%*d", "2", 42); // NON_COMPLIANT +} +void test_wrong_precision_type() { + printf("%.*d", 2, 42); // COMPLIANT + printf("%.*d", "2", 42); // NON_COMPLIANT +} +void test_incompatible_conversion() { + printf(""); // COMPLIANT + printf(""); // NON_COMPLIANT +} +void test_wrong_arg_type() { + printf("%d", 42); // COMPLIANT + printf("%s", 42); // NON_COMPLIANT +} +void test_wrong_arg_number() { + printf("%d", 42); // COMPLIANT + printf("%d, %s\n", 42); // NON_COMPLIANT + + printf("%*d", 2, 42); // COMPLIANT + printf("%*d", 42); // NON_COMPLIANT +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll index cbc27bec99..1b01fe2371 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll @@ -5,7 +5,12 @@ import codingstandards.cpp.exclusions.RuleMetadata newtype IO4Query = TToctouRaceConditionsWhileAccessingFilesQuery() or - TUseValidFormatStringsQuery() + TUseValidSpecifiersQuery() or + TWrongNumberOfFormatArgumentsQuery() or + TWrongTypeFormatArgumentsQuery() or + TUseValidSpecifierFlagsQuery() or + TUseValidSpecifierLengthQuery() or + TUseIntArgumentForWidthAndPrecisionQuery() predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { query = @@ -17,11 +22,51 @@ predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { ruleId = "FIO45-C" or query = - // `Query` instance for the `useValidFormatStrings` query - IO4Package::useValidFormatStringsQuery() and + // `Query` instance for the `useValidSpecifiers` query + IO4Package::useValidSpecifiersQuery() and queryId = - // `@id` for the `useValidFormatStrings` query - "c/cert/use-valid-format-strings" and + // `@id` for the `useValidSpecifiers` query + "c/cert/use-valid-specifiers" and + ruleId = "FIO47-C" + or + query = + // `Query` instance for the `wrongNumberOfFormatArguments` query + IO4Package::wrongNumberOfFormatArgumentsQuery() and + queryId = + // `@id` for the `wrongNumberOfFormatArguments` query + "c/cert/wrong-number-of-format-arguments" and + ruleId = "FIO47-C" + or + query = + // `Query` instance for the `wrongTypeFormatArguments` query + IO4Package::wrongTypeFormatArgumentsQuery() and + queryId = + // `@id` for the `wrongTypeFormatArguments` query + "c/cert/wrong-type-format-arguments" and + ruleId = "FIO47-C" + or + query = + // `Query` instance for the `useValidSpecifierFlags` query + IO4Package::useValidSpecifierFlagsQuery() and + queryId = + // `@id` for the `useValidSpecifierFlags` query + "c/cert/use-valid-specifier-flags" and + ruleId = "FIO47-C" + or + query = + // `Query` instance for the `useValidSpecifierLength` query + IO4Package::useValidSpecifierLengthQuery() and + queryId = + // `@id` for the `useValidSpecifierLength` query + "c/cert/use-valid-specifier-length" and + ruleId = "FIO47-C" + or + query = + // `Query` instance for the `useIntArgumentForWidthAndPrecision` query + IO4Package::useIntArgumentForWidthAndPrecisionQuery() and + queryId = + // `@id` for the `useIntArgumentForWidthAndPrecision` query + "c/cert/use-int-argument-for-width-and-precision" and ruleId = "FIO47-C" } @@ -33,10 +78,45 @@ module IO4Package { TQueryC(TIO4PackageQuery(TToctouRaceConditionsWhileAccessingFilesQuery())) } - Query useValidFormatStringsQuery() { + Query useValidSpecifiersQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useValidSpecifiers` query + TQueryC(TIO4PackageQuery(TUseValidSpecifiersQuery())) + } + + Query wrongNumberOfFormatArgumentsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `wrongNumberOfFormatArguments` query + TQueryC(TIO4PackageQuery(TWrongNumberOfFormatArgumentsQuery())) + } + + Query wrongTypeFormatArgumentsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `wrongTypeFormatArguments` query + TQueryC(TIO4PackageQuery(TWrongTypeFormatArgumentsQuery())) + } + + Query useValidSpecifierFlagsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useValidSpecifierFlags` query + TQueryC(TIO4PackageQuery(TUseValidSpecifierFlagsQuery())) + } + + Query useValidSpecifierLengthQuery() { + //autogenerate `Query` type + result = + // `Query` type for `useValidSpecifierLength` query + TQueryC(TIO4PackageQuery(TUseValidSpecifierLengthQuery())) + } + + Query useIntArgumentForWidthAndPrecisionQuery() { //autogenerate `Query` type result = - // `Query` type for `useValidFormatStrings` query - TQueryC(TIO4PackageQuery(TUseValidFormatStringsQuery())) + // `Query` type for `useIntArgumentForWidthAndPrecision` query + TQueryC(TIO4PackageQuery(TUseIntArgumentForWidthAndPrecisionQuery())) } } diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index ab1572603f..3daf836e20 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -29,12 +29,72 @@ }, "queries": [ { - "description": "Use valid format strings", + "description": "Use valid conversion specifier", "kind": "problem", "name": "Use valid format strings", "precision": "high", "severity": "error", - "short_name": "UseValidFormatStrings", + "short_name": "UseValidSpecifiers", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Use correct number of format arguments", + "kind": "problem", + "name": "Use correct number fo arguments", + "precision": "high", + "severity": "error", + "short_name": "WrongNumberOfFormatArguments", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Wrong type format arguments", + "kind": "problem", + "name": "Wrong type format arguments", + "precision": "high", + "severity": "error", + "short_name": "WrongTypeFormatArguments", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Use valid specifier flags", + "kind": "problem", + "name": "Use valid specifier flags", + "precision": "high", + "severity": "error", + "short_name": "UseValidSpecifierFlags", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Use valid specifier length", + "kind": "problem", + "name": "Use valid specifier length", + "precision": "high", + "severity": "error", + "short_name": "UseValidSpecifierLength", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Use `int` arguments for `width` and `precision`", + "kind": "problem", + "name": "Use `int` arguments for `width` and `precision`", + "precision": "high", + "severity": "error", + "short_name": "UseIntArgumentForWidthAndPrecision", "tags": [ "correctness", "security" From 0ba98f4487da6f97f8bc4a31be92f4f4e35d83b0 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 10 Aug 2022 19:45:35 +0200 Subject: [PATCH 05/31] Rule FIO47-C --- .../UseIntArgumentForWidthAndPrecision.md | 152 ---- .../UseIntArgumentForWidthAndPrecision.ql | 26 - .../rules/FIO47-C/UseValidSpecifierFlags.md | 152 ---- .../rules/FIO47-C/UseValidSpecifierFlags.ql | 32 - .../rules/FIO47-C/UseValidSpecifierLength.md | 152 ---- .../rules/FIO47-C/UseValidSpecifierLength.ql | 34 - .../src/rules/FIO47-C/UseValidSpecifiers.md | 7 +- .../src/rules/FIO47-C/UseValidSpecifiers.ql | 43 +- .../FIO47-C/WrongNumberOfFormatArguments.md | 7 +- .../rules/FIO47-C/WrongTypeFormatArguments.md | 7 +- ...seIntArgumentForWidthAndPrecision.expected | 1 - .../UseIntArgumentForWidthAndPrecision.qlref | 1 - .../FIO47-C/UseValidFormatStrings.expected | 1 - .../rules/FIO47-C/UseValidFormatStrings.qlref | 1 - .../FIO47-C/UseValidSpecifierFlags.expected | 1 - .../FIO47-C/UseValidSpecifierFlags.qlref | 1 - .../FIO47-C/UseValidSpecifierLength.expected | 1 - .../FIO47-C/UseValidSpecifierLength.qlref | 1 - .../rules/FIO47-C/UseValidSpecifiers.expected | 132 ++- .../WrongNumberOfFormatArguments.expected | 3 +- .../FIO47-C/WrongTypeFormatArguments.expected | 92 +- c/cert/test/rules/FIO47-C/test.c | 846 +++++++++++------- .../codingstandards/cpp/exclusions/c/IO4.qll | 50 +- rule_packages/c/IO4.json | 36 - 24 files changed, 773 insertions(+), 1006 deletions(-) delete mode 100644 c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md delete mode 100644 c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql delete mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md delete mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql delete mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md delete mode 100644 c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql delete mode 100644 c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected delete mode 100644 c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref delete mode 100644 c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected delete mode 100644 c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref delete mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected delete mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref delete mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected delete mode 100644 c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref diff --git a/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md b/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md deleted file mode 100644 index 06a6bfd6ae..0000000000 --- a/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.md +++ /dev/null @@ -1,152 +0,0 @@ -# FIO47-C: Use `int` arguments for `width` and `precision` - -This query implements the CERT-C rule FIO47-C: - -> Use valid format strings - - -## Description - -The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies - -> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. - - -Each *conversion specification* is introduced by the `%` character followed (in order) by - -* Zero or more *flags* (in any order), which modify the meaning of the conversion specification -* An optional minimum field *width* -* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier -* An optional *length modifier* that specifies the size of the argument -* A *conversion specifier character* that indicates the type of conversion to be applied -Common mistakes in creating format strings include -* Providing an incorrect number of arguments for the format string -* Using invalid conversion specifiers -* Using a flag character that is incompatible with the conversion specifier -* Using a length modifier that is incompatible with the conversion specifier -* Mismatching the argument type and conversion specifier -* Using an argument of type other than `int` for *width* or *precision* -The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). - -Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. - -Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) - -
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
-SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension - - -The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. - -Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. - -Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) - -## Noncompliant Code Example - -Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. - -```cpp -#include - -void func(void) { - const char *error_msg = "Resource not available to user."; - int error_type = 3; - /* ... */ - printf("Error (type %s): %d\n", error_type, error_msg); - /* ... */ -} -``` - -## Compliant Solution - -This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: - -```cpp -#include - -void func(void) { - const char *error_msg = "Resource not available to user."; - int error_type = 3; - /* ... */ - printf("Error (type %d): %s\n", error_type, error_msg); - - /* ... */ -} -``` - -## Risk Assessment - -Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). - -
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
- - -## Automated Detection - -
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
- - -## Related Vulnerabilities - -Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). - -## Related Guidelines - -[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) - -
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
- - -## CERT-CWE Mapping Notes - -[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes - -**CWE-686 and FIO47-C** - -Intersection( EXP37-C, FIO47-C) = - -* Invalid argument types passed to format I/O function -EXP37-C – FIO47-C = -* Invalid argument types passed to non-format I/O function -FIO47-C – EXP37-C = -* Invalid format string, but correctly matches arguments in number and type -Intersection( CWE-686, FIO47-C) = -* Use of format strings that do not match the type of arguments -CWE-686 – FIO47-C = -* Incorrect argument type in functions outside of the printf() family. -FIO47-C – CWE-686 = -* Invalid format strings that still match their arguments in type -**CWE-685 and FIO47-C** - -Intersection( CWE-685, FIO47-C) = - -* Use of format strings that do not match the number of arguments -CWE-685 – FIO47-C = -* Incorrect argument number in functions outside of the printf() family. -FIO47-C – CWE-685 = -* Invalid format strings that still match their arguments in number -**CWE-134 and FIO47-C** - -Intersection( FIO30-C, FIO47-C) = - -* Use of untrusted and ill-specified format string -FIO30-C – FIO47-C = -* Use of untrusted, but well-defined format string -FIO47-C – FIO30-C = -* Use of Ill-defined, but trusted format string -FIO47-C = Union(CWE-134, list) where list = -* Using a trusted but invalid format string - -## Bibliography - -
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
- - -## Implementation notes - -None - -## References - -* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql b/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql deleted file mode 100644 index 23cc91e1f4..0000000000 --- a/c/cert/src/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @id c/cert/use-int-argument-for-width-and-precision - * @name FIO47-C: Use `int` arguments for `width` and `precision` - * @description Use `int` arguments for `width` and `precision` - * @kind problem - * @precision high - * @problem.severity error - * @tags external/cert/id/fio47-c - * correctness - * security - * external/cert/obligation/rule - */ - -import cpp -import codingstandards.c.cert - -from FormatLiteral fl, Expr expr, string element -where - not isExcluded(expr, IO4Package::useIntArgumentForWidthAndPrecisionQuery()) and - ( - element = "minimum field width" and expr = fl.getUse().getMinFieldWidthArgument(_) - or - element = "precision" and expr = fl.getUse().getPrecisionArgument(_) - ) and - not expr.getType() instanceof IntType -select expr, "The coversion specification argument for '" + element + "' must be of type `int`." diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md b/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md deleted file mode 100644 index 1df2c220b7..0000000000 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.md +++ /dev/null @@ -1,152 +0,0 @@ -# FIO47-C: Use valid specifier flags - -This query implements the CERT-C rule FIO47-C: - -> Use valid format strings - - -## Description - -The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies - -> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. - - -Each *conversion specification* is introduced by the `%` character followed (in order) by - -* Zero or more *flags* (in any order), which modify the meaning of the conversion specification -* An optional minimum field *width* -* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier -* An optional *length modifier* that specifies the size of the argument -* A *conversion specifier character* that indicates the type of conversion to be applied -Common mistakes in creating format strings include -* Providing an incorrect number of arguments for the format string -* Using invalid conversion specifiers -* Using a flag character that is incompatible with the conversion specifier -* Using a length modifier that is incompatible with the conversion specifier -* Mismatching the argument type and conversion specifier -* Using an argument of type other than `int` for *width* or *precision* -The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). - -Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. - -Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) - -
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
-SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension - - -The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. - -Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. - -Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) - -## Noncompliant Code Example - -Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. - -```cpp -#include - -void func(void) { - const char *error_msg = "Resource not available to user."; - int error_type = 3; - /* ... */ - printf("Error (type %s): %d\n", error_type, error_msg); - /* ... */ -} -``` - -## Compliant Solution - -This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: - -```cpp -#include - -void func(void) { - const char *error_msg = "Resource not available to user."; - int error_type = 3; - /* ... */ - printf("Error (type %d): %s\n", error_type, error_msg); - - /* ... */ -} -``` - -## Risk Assessment - -Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). - -
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
- - -## Automated Detection - -
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
- - -## Related Vulnerabilities - -Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). - -## Related Guidelines - -[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) - -
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
- - -## CERT-CWE Mapping Notes - -[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes - -**CWE-686 and FIO47-C** - -Intersection( EXP37-C, FIO47-C) = - -* Invalid argument types passed to format I/O function -EXP37-C – FIO47-C = -* Invalid argument types passed to non-format I/O function -FIO47-C – EXP37-C = -* Invalid format string, but correctly matches arguments in number and type -Intersection( CWE-686, FIO47-C) = -* Use of format strings that do not match the type of arguments -CWE-686 – FIO47-C = -* Incorrect argument type in functions outside of the printf() family. -FIO47-C – CWE-686 = -* Invalid format strings that still match their arguments in type -**CWE-685 and FIO47-C** - -Intersection( CWE-685, FIO47-C) = - -* Use of format strings that do not match the number of arguments -CWE-685 – FIO47-C = -* Incorrect argument number in functions outside of the printf() family. -FIO47-C – CWE-685 = -* Invalid format strings that still match their arguments in number -**CWE-134 and FIO47-C** - -Intersection( FIO30-C, FIO47-C) = - -* Use of untrusted and ill-specified format string -FIO30-C – FIO47-C = -* Use of untrusted, but well-defined format string -FIO47-C – FIO30-C = -* Use of Ill-defined, but trusted format string -FIO47-C = Union(CWE-134, list) where list = -* Using a trusted but invalid format string - -## Bibliography - -
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
- - -## Implementation notes - -None - -## References - -* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql deleted file mode 100644 index 5a85a70a94..0000000000 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifierFlags.ql +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @id c/cert/use-valid-specifier-flags - * @name FIO47-C: Use valid specifier flags - * @description Use valid specifier flags - * @kind problem - * @precision high - * @problem.severity error - * @tags external/cert/id/fio47-c - * correctness - * security - * external/cert/obligation/rule - */ - -import cpp -import codingstandards.c.cert - -string getInvalidFlag(string specChar) { - specChar = ["d", "i", "u"] and result = ["#"] - or - specChar = ["o", "x", "X", "e", "E"] and result = ["'"] - or - specChar = ["c", "s", "p", "n", "C", "S", "%"] and result = ["'", "#", "0"] -} - -from FormatLiteral fl, string specChar, string flags -where - not isExcluded(fl, IO4Package::useValidSpecifierFlagsQuery()) and - flags = fl.getFlags(_) and - specChar = fl.getConversionChar(_) and - flags.matches("%" + getInvalidFlag(specChar) + "%") -select fl, - "The conversion specifier '" + specChar + "' is not compatible with flags '" + flags + "'." diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md b/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md deleted file mode 100644 index b97c25b273..0000000000 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.md +++ /dev/null @@ -1,152 +0,0 @@ -# FIO47-C: Use valid specifier length - -This query implements the CERT-C rule FIO47-C: - -> Use valid format strings - - -## Description - -The formatted output functions (`fprintf()` and related functions) convert, format, and print their arguments under control of a *format* string. The C Standard, 7.21.6.1, paragraph 3 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], specifies - -> The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The format is composed of zero or more directives: ordinary multibyte characters (not **%**), which are copied unchanged to the output stream; and conversion specifications, each of which results in fetching zero or more subsequent arguments, converting them, if applicable, according to the corresponding conversion specifier, and then writing the result to the output stream. - - -Each *conversion specification* is introduced by the `%` character followed (in order) by - -* Zero or more *flags* (in any order), which modify the meaning of the conversion specification -* An optional minimum field *width* -* An optional *precision* that gives the minimum number of digits, the maximum number of digits, or the maximum number of bytes, etc. depending on the conversion specifier -* An optional *length modifier* that specifies the size of the argument -* A *conversion specifier character* that indicates the type of conversion to be applied -Common mistakes in creating format strings include -* Providing an incorrect number of arguments for the format string -* Using invalid conversion specifiers -* Using a flag character that is incompatible with the conversion specifier -* Using a length modifier that is incompatible with the conversion specifier -* Mismatching the argument type and conversion specifier -* Using an argument of type other than `int` for *width* or *precision* -The following table summarizes the compliance of various conversion specifications. The first column contains one or more conversion specifier characters. The next four columns consider the combination of the specifier characters with the various flags (the apostrophe \[`'`\], `-`, `+`, the space character, `#`, and `0`). The next eight columns consider the combination of the specifier characters with the various length modifiers (`h`, `hh`, `l`, `ll`, `j`, `z`, `t`, and `L`). - -Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. - -Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) - -
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
-SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension - - -The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. - -Do not supply an unknown or invalid conversion specification or an invalid combination of flag character, precision, length modifier, or conversion specifier to a formatted IO function. Likewise, do not provide a number or type of argument that does not match the argument type of the conversion specifier used in the format string. - -Format strings are usually string literals specified at the call site, but they need not be. However, they should not contain [tainted values](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-taintedvalue). (See [FIO30-C. Exclude user input from format strings](https://wiki.sei.cmu.edu/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings) for more information.) - -## Noncompliant Code Example - -Mismatches between arguments and conversion specifications may result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Compilers may diagnose type mismatches in formatted output function invocations. In this noncompliant code example, the `error_type` argument to `printf()` is incorrectly matched with the `s` specifier rather than with the `d` specifier. Likewise, the `error_msg` argument is incorrectly matched with the `d` specifier instead of the `s` specifier. These usages result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). One possible result of this invocation is that `printf()` will interpret the `error_type` argument as a pointer and try to read a string from the address that `error_type` contains, possibly resulting in an access violation. - -```cpp -#include - -void func(void) { - const char *error_msg = "Resource not available to user."; - int error_type = 3; - /* ... */ - printf("Error (type %s): %d\n", error_type, error_msg); - /* ... */ -} -``` - -## Compliant Solution - -This compliant solution ensures that the arguments to the `printf()` function match their respective conversion specifications: - -```cpp -#include - -void func(void) { - const char *error_msg = "Resource not available to user."; - int error_type = 3; - /* ... */ - printf("Error (type %d): %s\n", error_type, error_msg); - - /* ... */ -} -``` - -## Risk Assessment - -Incorrectly specified format strings can result in memory corruption or [abnormal program termination](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-abnormaltermination). - -
Rule Severity Likelihood Remediation Cost Priority Level
FIO47-C High Unlikely Medium P6 L2
- - -## Automated Detection - -
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
- - -## Related Vulnerabilities - -Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+FIO47-C). - -## Related Guidelines - -[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) - -
Taxonomy Taxonomy item Relationship
CERT C FIO00-CPP. Take care when creating format strings Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961:2013 Using invalid format strings \[invfmtstr\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-686 , Function Call with Incorrect Argument Type 2017-06-29: CERT: Partial overlap
CWE 2.11 CWE-685 2017-06-29: CERT: Partial overlap
- - -## CERT-CWE Mapping Notes - -[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes - -**CWE-686 and FIO47-C** - -Intersection( EXP37-C, FIO47-C) = - -* Invalid argument types passed to format I/O function -EXP37-C – FIO47-C = -* Invalid argument types passed to non-format I/O function -FIO47-C – EXP37-C = -* Invalid format string, but correctly matches arguments in number and type -Intersection( CWE-686, FIO47-C) = -* Use of format strings that do not match the type of arguments -CWE-686 – FIO47-C = -* Incorrect argument type in functions outside of the printf() family. -FIO47-C – CWE-686 = -* Invalid format strings that still match their arguments in type -**CWE-685 and FIO47-C** - -Intersection( CWE-685, FIO47-C) = - -* Use of format strings that do not match the number of arguments -CWE-685 – FIO47-C = -* Incorrect argument number in functions outside of the printf() family. -FIO47-C – CWE-685 = -* Invalid format strings that still match their arguments in number -**CWE-134 and FIO47-C** - -Intersection( FIO30-C, FIO47-C) = - -* Use of untrusted and ill-specified format string -FIO30-C – FIO47-C = -* Use of untrusted, but well-defined format string -FIO47-C – FIO30-C = -* Use of Ill-defined, but trusted format string -FIO47-C = Union(CWE-134, list) where list = -* Using a trusted but invalid format string - -## Bibliography - -
\[ ISO/IEC 9899:2011 \] Subclause 7.21.6.1, "The fprintf Function"
- - -## Implementation notes - -None - -## References - -* CERT-C: [FIO47-C: Use valid format strings](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql deleted file mode 100644 index f5b8245812..0000000000 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifierLength.ql +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @id c/cert/use-valid-specifier-length - * @name FIO47-C: Use valid specifier length - * @description Use valid specifier length - * @kind problem - * @precision high - * @problem.severity error - * @tags external/cert/id/fio47-c - * correctness - * security - * external/cert/obligation/rule - */ - -import cpp -import codingstandards.c.cert - -string getInvalidLength(string specChar) { - specChar = ["d", "i", "o", "u", "n"] and result = ["L"] - or - specChar = ["f", "F", "e", "E", "g", "G", "a", "A"] and result = ["h", "hh", "j", "z", "t"] - or - specChar = ["c", "s"] and result = ["h", "hh", "ll", "j", "z", "t", "L"] - or - specChar = ["p", "C", "S", "%"] and result = ["h", "hh", "l", "ll", "j", "z", "t", "L"] -} - -from FormatLiteral fl, string specChar, string length -where - not isExcluded(fl, IO4Package::useValidSpecifierLengthQuery()) and - length = fl.getLength(_) and - specChar = fl.getConversionChar(_) and - length.matches("%" + getInvalidLength(specChar) + "%") -select fl, - "The conversion specifier '" + specChar + "' is not compatible with length '" + length + "'." diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md index 46df2d9465..13aa2f3471 100644 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md @@ -32,8 +32,9 @@ Valid combinations are marked with a type name; arguments matched with the conve Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) -
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
-SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension +

Conversion
Specifier
Character

' XSI

-
+
SPACE


#


0


h


hh


l


ll


j


z


t


L

Argument
Type

d, i

(tick)

(tick)

(error)

(tick)

short

signed char

long

long long

intmax_t

size_t

ptrdiff_t

(error)

Signed integer

o

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

u

(tick)

(tick)

(error)

(tick)

unsigned short

unsigned  char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

x, X

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

f, F

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

e, E

(error)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

g, G

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

a, A

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

c

(error)

(tick)

(error)

(error)

(error)

(error)

wint_t

(error)

(error)

(error)

(error)

(error)

int or wint_t

s

(error)

(tick)

(error)

(error)

(error)

(error)

NTWS

(error)

(error)

(error)

(error)

(error)

NTBS or NTWS

p

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

void*

n

(error)

(error)

(error)

(error)

short*

char*

long*

long long*

intmax_t*

size_t*

ptrdiff_t*

(error)

Pointer to integer

C XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

wint_t

S XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

NTWS

%

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

None

+ +SPACE: The space (" ") character
     N/E: No effect
     NTBS: char* argument pointing to a null-terminated character string
     NTWS: wchar_t* argument pointing to a null-terminated wide character string
     XSI: ISO/IEC 9945-2003 XSI extension The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. @@ -84,7 +85,7 @@ Incorrectly specified format strings can result in memory corruption or [abnorma ## Automated Detection -
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.20 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
## Related Vulnerabilities diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql index ad98f3d7a0..cafe5a6454 100644 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql @@ -14,10 +14,43 @@ import cpp import codingstandards.c.cert -class InvalidSpecifier extends FormatLiteral { - InvalidSpecifier() { not this.specsAreKnown() } +string getInvalidFlag(string specChar) { + specChar = ["d", "i", "u"] and result = ["#"] + or + specChar = ["o", "x", "X", "e", "E"] and result = ["'"] + or + specChar = ["c", "s", "p", "C", "S", "%"] and result = ["'", "#", "0"] + or + specChar = ["n"] and result = ["'", "-", "+", " ", "#", "0"] } -from InvalidSpecifier x -where not isExcluded(x, IO4Package::useValidSpecifiersQuery()) -select x, "The conversion specifier '" + x + "' is not valid." +string getInvalidLength(string specChar) { + specChar = ["d", "i", "o", "u", "x", "X", "n"] and result = ["L"] + or + specChar = ["f", "F", "e", "E", "g", "G", "a", "A"] and result = ["h", "hh", "j", "z", "t"] + or + specChar = ["c", "s"] and result = ["h", "hh", "ll", "j", "z", "t", "L"] + or + specChar = ["p", "C", "S", "%"] and result = ["h", "hh", "l", "ll", "j", "z", "t", "L"] +} + +from FormatLiteral x, string message, string compatible, string specChar +where + not isExcluded(x, IO4Package::useValidSpecifiersQuery()) and + message = "The conversion specifier '" + x + "' is not valid." and + compatible = "" and + specChar = "" and + not x.specsAreKnown() + or + message = + "The conversion specifier '" + specChar + "' is not compatible with flags '" + compatible + "'" and + compatible = x.getFlags(_) and + specChar = x.getConversionChar(_) and + compatible.matches("%" + getInvalidFlag(specChar) + "%") + or + message = + "The conversion specifier '" + specChar + "' is not compatible with length '" + compatible + "'" and + compatible = x.getLength(_) and + specChar = x.getConversionChar(_) and + compatible.matches("%" + getInvalidLength(specChar) + "%") +select x, message diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md index 3025b0c631..d53e6337b6 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md @@ -32,8 +32,9 @@ Valid combinations are marked with a type name; arguments matched with the conve Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) -
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
-SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension +

Conversion
Specifier
Character

' XSI

-
+
SPACE


#


0


h


hh


l


ll


j


z


t


L

Argument
Type

d, i

(tick)

(tick)

(error)

(tick)

short

signed char

long

long long

intmax_t

size_t

ptrdiff_t

(error)

Signed integer

o

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

u

(tick)

(tick)

(error)

(tick)

unsigned short

unsigned  char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

x, X

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

f, F

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

e, E

(error)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

g, G

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

a, A

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

c

(error)

(tick)

(error)

(error)

(error)

(error)

wint_t

(error)

(error)

(error)

(error)

(error)

int or wint_t

s

(error)

(tick)

(error)

(error)

(error)

(error)

NTWS

(error)

(error)

(error)

(error)

(error)

NTBS or NTWS

p

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

void*

n

(error)

(error)

(error)

(error)

short*

char*

long*

long long*

intmax_t*

size_t*

ptrdiff_t*

(error)

Pointer to integer

C XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

wint_t

S XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

NTWS

%

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

None

+ +SPACE: The space (" ") character
     N/E: No effect
     NTBS: char* argument pointing to a null-terminated character string
     NTWS: wchar_t* argument pointing to a null-terminated wide character string
     XSI: ISO/IEC 9945-2003 XSI extension The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. @@ -84,7 +85,7 @@ Incorrectly specified format strings can result in memory corruption or [abnorma ## Automated Detection -
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.20 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
## Related Vulnerabilities diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md index de84d18d6c..4f5f5d74be 100644 --- a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md @@ -32,8 +32,9 @@ Valid combinations are marked with a type name; arguments matched with the conve Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) -
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
-SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension +

Conversion
Specifier
Character

' XSI

-
+
SPACE


#


0


h


hh


l


ll


j


z


t


L

Argument
Type

d, i

(tick)

(tick)

(error)

(tick)

short

signed char

long

long long

intmax_t

size_t

ptrdiff_t

(error)

Signed integer

o

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

u

(tick)

(tick)

(error)

(tick)

unsigned short

unsigned  char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

x, X

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

f, F

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

e, E

(error)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

g, G

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

a, A

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

c

(error)

(tick)

(error)

(error)

(error)

(error)

wint_t

(error)

(error)

(error)

(error)

(error)

int or wint_t

s

(error)

(tick)

(error)

(error)

(error)

(error)

NTWS

(error)

(error)

(error)

(error)

(error)

NTBS or NTWS

p

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

void*

n

(error)

(error)

(error)

(error)

short*

char*

long*

long long*

intmax_t*

size_t*

ptrdiff_t*

(error)

Pointer to integer

C XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

wint_t

S XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

NTWS

%

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

None

+ +SPACE: The space (" ") character
     N/E: No effect
     NTBS: char* argument pointing to a null-terminated character string
     NTWS: wchar_t* argument pointing to a null-terminated wide character string
     XSI: ISO/IEC 9945-2003 XSI extension The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. @@ -84,7 +85,7 @@ Incorrectly specified format strings can result in memory corruption or [abnorma ## Automated Detection -
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.19 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
+
Tool Version Checker Description
Axivion Bauhaus Suite 7.2.0 CertC-FIO47 Fully implemented
CodeSonar 7.0p0 IO.INJ.FMT MISC.FMTMISC.FMTTYPE Format string injection Format string Format string type error
Coverity 2017.07 PW Reports when the number of arguments differs from the number of required arguments according to the format string
GCC 4.3.5 Can detect violations of this recommendation when the -Wformat flag is used
Helix QAC 2022.2 C0161, C0162, C0163, C0164, C0165, C0166, C0167, C0168, C0169, C0170, C0171, C0172, C0173, C0174, C0175, C0176, C0177, C0178, C0179, C0180, C0184, C0185, C0190, C0191, C0192, C0193, C0194, C0195, C0196, C0197, C0198, C0199, C0200, C0201, C0202, C0204, C0206, C0209 C++3150, C++3151, C++3152, C++3153, C++3154, C++3155, C++3156, C++3157, C++3158, C++3159
Klocwork 2022.2 SV.FMT_STR.PRINT_FORMAT_MISMATCH.BAD SV.FMT_STR.PRINT_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.PRINT_IMPROP_LENGTH SV.FMT_STR.PRINT_PARAMS_WRONGNUM.FEW SV.FMT_STR.PRINT_PARAMS_WRONGNUM.MANY SV.FMT_STR.SCAN_FORMAT_MISMATCH.BAD SV.FMT_STR.SCAN_FORMAT_MISMATCH.UNDESIRED SV.FMT_STR.SCAN_IMPROP_LENGTH SV.FMT_STR.SCAN_PARAMS_WRONGNUM.FEW SV.FMT_STR.SCAN_PARAMS_WRONGNUM.MANY SV.FMT_STR.UNKWN_FORMAT
LDRA tool suite 9.7.1 486 S 589 S Fully implemented
Parasoft C/C++test 2022.1 CERT_C-FIO47-a CERT_C-FIO47-b CERT_C-FIO47-c CERT_C-FIO47-d CERT_C-FIO47-e CERT_C-FIO47-f There should be no mismatch between the '%s' and '%c' format specifiers in the format string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%f' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%i' and '%d' format specifiers in the string and their corresponding arguments in the invocation of a string formatting function There should be no mismatch between the '%u' format specifier in the format string and its corresponding argument in the invocation of a string formatting function There should be no mismatch between the '%p' format specifier in the format string and its corresponding argument in the invocation of a string formatting function The number of format specifiers in the format string and the number of corresponding arguments in the invocation of a string formatting function should be equal
PC-lint Plus 1.4 492, 493, 494, 499, 557, 558, 559, 566, 705, 706, 719, 816, 855, 2401, 2402,2403, 2404, 2405, 2406, 2407 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule FIO47-C Check for format string specifiers and arguments mismatch (rule fully covered)
PRQA QA-C 9.7 0161, 0162, 0163, 0164, 0165, 0166, 0167, 0168, 0169, 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177, 0178, 0179 \[U\], 0180 \[C99\], 0184 \[U\], 0185 \[U\], 0190 \[U\], 0191 \[U\], 0192 \[U\], 0193 \[U\], 0194 \[U\], 0195 \[U\], 0196 \[U\], 0197 \[U\], 0198 \[U\], 0199 \[U\], 0200 \[U\], 0201 \[U\], 0202 \[I\], 0204 \[U\], 0206 \[U\] Partially implemented
PVS-Studio 7.20 V510 , V576
TrustInSoft Analyzer 1.38 match format and arguments Exhaustively verified (see the compliant and the non-compliant example ).
## Related Vulnerabilities diff --git a/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected b/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected deleted file mode 100644 index 2ec1a0ac6c..0000000000 --- a/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.expected +++ /dev/null @@ -1 +0,0 @@ -No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref b/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref deleted file mode 100644 index cf4bd0bc3f..0000000000 --- a/c/cert/test/rules/FIO47-C/UseIntArgumentForWidthAndPrecision.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/FIO47-C/UseIntArgumentForWidthAndPrecision.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected b/c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected deleted file mode 100644 index 2ec1a0ac6c..0000000000 --- a/c/cert/test/rules/FIO47-C/UseValidFormatStrings.expected +++ /dev/null @@ -1 +0,0 @@ -No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref b/c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref deleted file mode 100644 index 7e6481309d..0000000000 --- a/c/cert/test/rules/FIO47-C/UseValidFormatStrings.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/FIO47-C/UseValidFormatStrings.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected deleted file mode 100644 index 2ec1a0ac6c..0000000000 --- a/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.expected +++ /dev/null @@ -1 +0,0 @@ -No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref b/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref deleted file mode 100644 index cb97d649a8..0000000000 --- a/c/cert/test/rules/FIO47-C/UseValidSpecifierFlags.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/FIO47-C/UseValidSpecifierFlags.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected deleted file mode 100644 index 2ec1a0ac6c..0000000000 --- a/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.expected +++ /dev/null @@ -1 +0,0 @@ -No expected results have yet been specified \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref b/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref deleted file mode 100644 index 9c31c9f49d..0000000000 --- a/c/cert/test/rules/FIO47-C/UseValidSpecifierLength.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/FIO47-C/UseValidSpecifierLength.ql \ No newline at end of file diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected index 2ec1a0ac6c..5367f2ce51 100644 --- a/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected @@ -1 +1,131 @@ -No expected results have yet been specified \ No newline at end of file +| test.c:60:10:60:13 | %b | The conversion specifier '%b' is not valid. | +| test.c:65:10:65:16 | %'-#d | The conversion specifier 'd' is not compatible with flags ''-#' | +| test.c:71:10:71:14 | %#d | The conversion specifier 'd' is not compatible with flags '#' | +| test.c:78:10:78:14 | %#i | The conversion specifier 'i' is not compatible with flags '#' | +| test.c:81:10:81:14 | %'o | The conversion specifier 'o' is not compatible with flags ''' | +| test.c:92:10:92:14 | %#u | The conversion specifier 'u' is not compatible with flags '#' | +| test.c:95:10:95:14 | %'x | The conversion specifier 'x' is not compatible with flags ''' | +| test.c:102:10:102:14 | %'X | The conversion specifier 'X' is not compatible with flags ''' | +| test.c:123:10:123:14 | %'e | The conversion specifier 'e' is not compatible with flags ''' | +| test.c:130:10:130:14 | %'E | The conversion specifier 'E' is not compatible with flags ''' | +| test.c:165:10:165:14 | %'c | The conversion specifier 'c' is not compatible with flags ''' | +| test.c:169:10:169:14 | %#c | The conversion specifier 'c' is not compatible with flags '#' | +| test.c:170:10:170:14 | %0c | The conversion specifier 'c' is not compatible with flags '0' | +| test.c:172:10:172:14 | %'s | The conversion specifier 's' is not compatible with flags ''' | +| test.c:176:10:176:14 | %#s | The conversion specifier 's' is not compatible with flags '#' | +| test.c:177:10:177:14 | %0s | The conversion specifier 's' is not compatible with flags '0' | +| test.c:179:10:179:14 | %'p | The conversion specifier 'p' is not compatible with flags ''' | +| test.c:183:10:183:14 | %#p | The conversion specifier 'p' is not compatible with flags '#' | +| test.c:184:10:184:14 | %0p | The conversion specifier 'p' is not compatible with flags '0' | +| test.c:186:10:186:14 | %'n | The conversion specifier 'n' is not compatible with flags ''' | +| test.c:187:10:187:14 | %-n | The conversion specifier 'n' is not compatible with flags '-' | +| test.c:188:10:188:14 | %+n | The conversion specifier 'n' is not compatible with flags '+' | +| test.c:189:10:189:14 | % n | The conversion specifier 'n' is not compatible with flags ' ' | +| test.c:190:10:190:14 | %#n | The conversion specifier 'n' is not compatible with flags '#' | +| test.c:191:10:191:14 | %0n | The conversion specifier 'n' is not compatible with flags '0' | +| test.c:193:10:193:14 | %'C | The conversion specifier 'C' is not compatible with flags ''' | +| test.c:197:10:197:14 | %#C | The conversion specifier 'C' is not compatible with flags '#' | +| test.c:198:10:198:14 | %0C | The conversion specifier 'C' is not compatible with flags '0' | +| test.c:200:10:200:14 | %'S | The conversion specifier 'S' is not compatible with flags ''' | +| test.c:204:10:204:14 | %#S | The conversion specifier 'S' is not compatible with flags '#' | +| test.c:205:10:205:14 | %0S | The conversion specifier 'S' is not compatible with flags '0' | +| test.c:208:10:208:14 | %'% | The conversion specifier '%'%' is not valid. | +| test.c:209:10:209:14 | %-% | The conversion specifier '%-%' is not valid. | +| test.c:210:10:210:14 | %+% | The conversion specifier '%+%' is not valid. | +| test.c:211:10:211:14 | % % | The conversion specifier '% %' is not valid. | +| test.c:212:10:212:14 | %#% | The conversion specifier '%#%' is not valid. | +| test.c:213:10:213:14 | %0% | The conversion specifier '%0%' is not valid. | +| test.c:214:10:214:14 | %0% | The conversion specifier '%0%' is not valid. | +| test.c:225:10:225:14 | %Ld | The conversion specifier 'd' is not compatible with length 'L' | +| test.c:234:10:234:14 | %Li | The conversion specifier 'i' is not compatible with length 'L' | +| test.c:243:10:243:14 | %Lo | The conversion specifier 'o' is not compatible with length 'L' | +| test.c:252:10:252:14 | %Lu | The conversion specifier 'u' is not compatible with length 'L' | +| test.c:261:10:261:14 | %Lx | The conversion specifier 'x' is not compatible with length 'L' | +| test.c:270:10:270:14 | %LX | The conversion specifier 'X' is not compatible with length 'L' | +| test.c:272:10:272:14 | %hf | The conversion specifier 'f' is not compatible with length 'h' | +| test.c:273:10:273:15 | %hhf | The conversion specifier 'f' is not compatible with length 'hh' | +| test.c:276:10:276:14 | %jf | The conversion specifier 'f' is not compatible with length 'j' | +| test.c:277:10:277:14 | %zf | The conversion specifier 'f' is not compatible with length 'z' | +| test.c:278:10:278:14 | %tf | The conversion specifier 'f' is not compatible with length 't' | +| test.c:281:10:281:14 | %hF | The conversion specifier 'F' is not compatible with length 'h' | +| test.c:282:10:282:15 | %hhF | The conversion specifier 'F' is not compatible with length 'hh' | +| test.c:285:10:285:14 | %jF | The conversion specifier 'F' is not compatible with length 'j' | +| test.c:286:10:286:14 | %zF | The conversion specifier 'F' is not compatible with length 'z' | +| test.c:287:10:287:14 | %tF | The conversion specifier 'F' is not compatible with length 't' | +| test.c:290:10:290:14 | %he | The conversion specifier 'e' is not compatible with length 'h' | +| test.c:291:10:291:15 | %hhe | The conversion specifier 'e' is not compatible with length 'hh' | +| test.c:294:10:294:14 | %je | The conversion specifier 'e' is not compatible with length 'j' | +| test.c:295:10:295:14 | %ze | The conversion specifier 'e' is not compatible with length 'z' | +| test.c:296:10:296:14 | %te | The conversion specifier 'e' is not compatible with length 't' | +| test.c:299:10:299:14 | %hE | The conversion specifier 'E' is not compatible with length 'h' | +| test.c:300:10:300:15 | %hhE | The conversion specifier 'E' is not compatible with length 'hh' | +| test.c:303:10:303:14 | %jE | The conversion specifier 'E' is not compatible with length 'j' | +| test.c:304:10:304:14 | %zE | The conversion specifier 'E' is not compatible with length 'z' | +| test.c:305:10:305:14 | %tE | The conversion specifier 'E' is not compatible with length 't' | +| test.c:308:10:308:14 | %hg | The conversion specifier 'g' is not compatible with length 'h' | +| test.c:309:10:309:15 | %hhg | The conversion specifier 'g' is not compatible with length 'hh' | +| test.c:312:10:312:14 | %jg | The conversion specifier 'g' is not compatible with length 'j' | +| test.c:313:10:313:14 | %zg | The conversion specifier 'g' is not compatible with length 'z' | +| test.c:314:10:314:14 | %tg | The conversion specifier 'g' is not compatible with length 't' | +| test.c:317:10:317:14 | %hG | The conversion specifier 'G' is not compatible with length 'h' | +| test.c:318:10:318:15 | %hhG | The conversion specifier 'G' is not compatible with length 'hh' | +| test.c:321:10:321:14 | %jG | The conversion specifier 'G' is not compatible with length 'j' | +| test.c:322:10:322:14 | %zG | The conversion specifier 'G' is not compatible with length 'z' | +| test.c:323:10:323:14 | %tG | The conversion specifier 'G' is not compatible with length 't' | +| test.c:326:10:326:14 | %ha | The conversion specifier 'a' is not compatible with length 'h' | +| test.c:327:10:327:15 | %hha | The conversion specifier 'a' is not compatible with length 'hh' | +| test.c:330:10:330:14 | %ja | The conversion specifier 'a' is not compatible with length 'j' | +| test.c:331:10:331:14 | %za | The conversion specifier 'a' is not compatible with length 'z' | +| test.c:332:10:332:14 | %ta | The conversion specifier 'a' is not compatible with length 't' | +| test.c:335:10:335:14 | %hA | The conversion specifier 'A' is not compatible with length 'h' | +| test.c:336:10:336:15 | %hhA | The conversion specifier 'A' is not compatible with length 'hh' | +| test.c:339:10:339:14 | %jA | The conversion specifier 'A' is not compatible with length 'j' | +| test.c:340:10:340:14 | %zA | The conversion specifier 'A' is not compatible with length 'z' | +| test.c:341:10:341:14 | %tA | The conversion specifier 'A' is not compatible with length 't' | +| test.c:344:10:344:14 | %hc | The conversion specifier 'c' is not compatible with length 'h' | +| test.c:345:10:345:15 | %hhc | The conversion specifier 'c' is not compatible with length 'hh' | +| test.c:347:10:347:15 | %llc | The conversion specifier 'c' is not compatible with length 'll' | +| test.c:348:10:348:14 | %jc | The conversion specifier 'c' is not compatible with length 'j' | +| test.c:349:10:349:14 | %zc | The conversion specifier 'c' is not compatible with length 'z' | +| test.c:350:10:350:14 | %tc | The conversion specifier 'c' is not compatible with length 't' | +| test.c:351:10:351:14 | %Lc | The conversion specifier 'c' is not compatible with length 'L' | +| test.c:353:10:353:14 | %hs | The conversion specifier 's' is not compatible with length 'h' | +| test.c:354:10:354:15 | %hhs | The conversion specifier 's' is not compatible with length 'hh' | +| test.c:356:10:356:15 | %lls | The conversion specifier 's' is not compatible with length 'll' | +| test.c:357:10:357:14 | %js | The conversion specifier 's' is not compatible with length 'j' | +| test.c:358:10:358:14 | %zs | The conversion specifier 's' is not compatible with length 'z' | +| test.c:359:10:359:14 | %ts | The conversion specifier 's' is not compatible with length 't' | +| test.c:360:10:360:14 | %Ls | The conversion specifier 's' is not compatible with length 'L' | +| test.c:362:10:362:14 | %hp | The conversion specifier 'p' is not compatible with length 'h' | +| test.c:363:10:363:15 | %hhp | The conversion specifier 'p' is not compatible with length 'hh' | +| test.c:364:10:364:14 | %lp | The conversion specifier 'p' is not compatible with length 'l' | +| test.c:365:10:365:15 | %llp | The conversion specifier 'p' is not compatible with length 'll' | +| test.c:366:10:366:14 | %jp | The conversion specifier 'p' is not compatible with length 'j' | +| test.c:367:10:367:14 | %zp | The conversion specifier 'p' is not compatible with length 'z' | +| test.c:368:10:368:14 | %tp | The conversion specifier 'p' is not compatible with length 't' | +| test.c:369:10:369:14 | %Lp | The conversion specifier 'p' is not compatible with length 'L' | +| test.c:378:10:378:14 | %Ln | The conversion specifier 'n' is not compatible with length 'L' | +| test.c:380:10:380:14 | %hC | The conversion specifier 'C' is not compatible with length 'h' | +| test.c:381:10:381:15 | %hhC | The conversion specifier 'C' is not compatible with length 'hh' | +| test.c:382:10:382:14 | %lC | The conversion specifier 'C' is not compatible with length 'l' | +| test.c:383:10:383:15 | %llC | The conversion specifier 'C' is not compatible with length 'll' | +| test.c:384:10:384:14 | %jC | The conversion specifier 'C' is not compatible with length 'j' | +| test.c:385:10:385:14 | %zC | The conversion specifier 'C' is not compatible with length 'z' | +| test.c:386:10:386:14 | %tC | The conversion specifier 'C' is not compatible with length 't' | +| test.c:387:10:387:14 | %LC | The conversion specifier 'C' is not compatible with length 'L' | +| test.c:389:10:389:14 | %hS | The conversion specifier 'S' is not compatible with length 'h' | +| test.c:390:10:390:15 | %hhS | The conversion specifier 'S' is not compatible with length 'hh' | +| test.c:391:10:391:14 | %lS | The conversion specifier 'S' is not compatible with length 'l' | +| test.c:392:10:392:15 | %llS | The conversion specifier 'S' is not compatible with length 'll' | +| test.c:393:10:393:14 | %jS | The conversion specifier 'S' is not compatible with length 'j' | +| test.c:394:10:394:14 | %zS | The conversion specifier 'S' is not compatible with length 'z' | +| test.c:395:10:395:14 | %tS | The conversion specifier 'S' is not compatible with length 't' | +| test.c:396:10:396:14 | %LS | The conversion specifier 'S' is not compatible with length 'L' | +| test.c:398:10:398:14 | %h% | The conversion specifier '%h%' is not valid. | +| test.c:399:10:399:15 | %hh% | The conversion specifier '%hh%' is not valid. | +| test.c:400:10:400:14 | %l% | The conversion specifier '%l%' is not valid. | +| test.c:401:10:401:15 | %ll% | The conversion specifier '%ll%' is not valid. | +| test.c:402:10:402:14 | %j% | The conversion specifier '%j%' is not valid. | +| test.c:403:10:403:14 | %z% | The conversion specifier '%z%' is not valid. | +| test.c:404:10:404:14 | %t% | The conversion specifier '%t%' is not valid. | +| test.c:405:10:405:14 | %L% | The conversion specifier '%L%' is not valid. | diff --git a/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected index 2ec1a0ac6c..1ea0fa4369 100644 --- a/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected +++ b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected @@ -1 +1,2 @@ -No expected results have yet been specified \ No newline at end of file +| test.c:540:3:540:8 | call to printf | Format expects 2 arguments but given 1 | +| test.c:543:3:543:8 | call to printf | Format expects 2 arguments but given 1 | diff --git a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected index 2ec1a0ac6c..eb4161b5c3 100644 --- a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected @@ -1 +1,91 @@ -No expected results have yet been specified \ No newline at end of file +| test.c:375:17:375:30 | v_intmax_t_ptr | This argument should be of type 'int *' but is of type 'signed long *' | +| test.c:376:17:376:28 | v_size_t_ptr | This argument should be of type 'int *' but is of type 'unsigned long *' | +| test.c:377:17:377:31 | v_ptrdiff_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:414:17:414:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:418:18:418:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:422:16:422:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:423:17:423:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:424:18:424:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:425:17:425:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:426:18:426:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:428:17:428:26 | v_char_ptr | This argument should be of type 'ssize_t' but is of type 'char *' | +| test.c:429:17:429:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:431:16:431:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:432:17:432:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:433:18:433:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:434:17:434:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:435:18:435:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:437:17:437:26 | v_char_ptr | This argument should be of type 'ssize_t' but is of type 'char *' | +| test.c:438:17:438:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:440:16:440:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:441:17:441:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:442:18:442:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:443:17:443:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:444:18:444:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:446:17:446:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:449:16:449:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:450:17:450:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:451:18:451:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:452:17:452:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:453:18:453:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:455:17:455:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:458:16:458:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:459:17:459:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:460:18:460:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:461:17:461:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:462:18:462:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:464:17:464:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:467:16:467:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:468:17:468:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:469:18:469:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:470:17:470:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:471:18:471:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:473:17:473:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:476:16:476:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:477:17:477:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:478:18:478:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:479:17:479:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:481:16:481:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:482:17:482:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:483:18:483:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:484:17:484:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:486:16:486:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:487:17:487:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:488:18:488:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:489:17:489:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:491:16:491:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:492:17:492:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:493:18:493:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:494:17:494:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:496:16:496:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:497:17:497:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:498:18:498:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:499:17:499:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:501:16:501:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:502:17:502:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:503:18:503:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:504:17:504:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:506:16:506:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:507:17:507:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:508:18:508:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:509:17:509:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:511:16:511:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:512:17:512:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:513:18:513:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:514:17:514:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:516:16:516:25 | v_char_ptr | This argument should be of type 'char' but is of type 'char *' | +| test.c:517:17:517:26 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:519:16:519:20 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:520:17:520:21 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | +| test.c:522:16:522:20 | v_int | This argument should be of type 'void *' but is of type 'int' | +| test.c:524:16:524:20 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:525:17:525:21 | v_int | This argument should be of type 'short *' but is of type 'int' | +| test.c:526:18:526:22 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:527:17:527:21 | v_int | This argument should be of type 'long *' but is of type 'int' | +| test.c:527:17:527:21 | v_int | This argument should be of type 'signed long *' but is of type 'int' | +| test.c:528:18:528:22 | v_int | This argument should be of type 'long long *' but is of type 'int' | +| test.c:529:17:529:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:530:17:530:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:531:17:531:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:533:16:533:25 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:535:16:535:20 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | diff --git a/c/cert/test/rules/FIO47-C/test.c b/c/cert/test/rules/FIO47-C/test.c index 899d9cfb80..f1d26be73c 100644 --- a/c/cert/test/rules/FIO47-C/test.c +++ b/c/cert/test/rules/FIO47-C/test.c @@ -1,4 +1,36 @@ +#include +#include #include +#include + +signed int v_signed_int = 42; +short v_short = 42; +signed char v_signed_char = 42; +long v_long = 42; +long long v_long_long = 42; +intmax_t v_intmax_t = 42; +size_t v_size_t = 42; +ptrdiff_t v_ptrdiff_t = 42; +unsigned int v_unsigned_int = 42; +unsigned short v_unsigned_short = 42; +unsigned char v_unsigned_char = 42; +unsigned long v_unsigned_long = 42; +unsigned long long v_unsigned_long_long = 42; +uintmax_t v_uintmax_t = 42; +double v_double = 42; +long double v_long_double = 42; +int v_int = 42; +wint_t v_wint_t = 42; +char *v_char_ptr = "42"; +wchar_t *v_wchar_t_ptr = L"42"; +void *v_void_ptr = &v_signed_int; +int *v_int_ptr = &v_int; +short *v_short_ptr = &v_short; +long *v_long_ptr = &v_long; +long long *v_long_long_ptr = &v_long_long; +intmax_t *v_intmax_t_ptr = &v_intmax_t; +size_t *v_size_t_ptr = &v_size_t; +ptrdiff_t *v_ptrdiff_t_ptr = &v_ptrdiff_t; void f1() { const char *s = "Hello"; @@ -27,364 +59,482 @@ void test_invalid_specifier() { printf("%d", 42); // COMPLIANT printf("%b", 42); // NON_COMPLIANT } + void test_incompatible_flag() { - printf("%'-d", 42); // COMPLIANT - printf("%'-#d", 42); // NON_COMPLIANT - - printf("%'d", 42); // COMPLIANT - printf("%-d", 42); // COMPLIANT - printf("%+d", 42); // COMPLIANT - printf("% d", 42); // COMPLIANT - printf("%#d", 42); // NON_COMPLIANT - printf("%0d", 42); // COMPLIANT - - printf("%'i", 42); // COMPLIANT - printf("%-i", 42); // COMPLIANT - printf("%+i", 42); // COMPLIANT - printf("% i", 42); // COMPLIANT - printf("%#i", 42); // NON_COMPLIANT - printf("%0i", 42); // COMPLIANT - - printf("%'o", 42); // NON_COMPLIANT - printf("%-o", 42); // COMPLIANT - printf("%+o", 42); // COMPLIANT - printf("% o", 42); // COMPLIANT - printf("%#o", 42); // COMPLIANT - printf("%0o", 42); // COMPLIANT - - printf("%'u", 42); // COMPLIANT - printf("%-u", 42); // COMPLIANT - printf("%+u", 42); // COMPLIANT - printf("% u", 42); // COMPLIANT - printf("%#u", 42); // NON_COMPLIANT - printf("%0u", 42); // COMPLIANT - - printf("%'x", 42); // NON_COMPLIANT - printf("%-x", 42); // COMPLIANT - printf("%+x", 42); // COMPLIANT - printf("% x", 42); // COMPLIANT - printf("%#x", 42); // COMPLIANT - printf("%0x", 42); // COMPLIANT - - printf("%'X", 42); // NON_COMPLIANT - printf("%-X", 42); // COMPLIANT - printf("%+X", 42); // COMPLIANT - printf("% X", 42); // COMPLIANT - printf("%#X", 42); // COMPLIANT - printf("%0X", 42); // COMPLIANT - - printf("%'f", 42); // COMPLIANT - printf("%-f", 42); // COMPLIANT - printf("%+f", 42); // COMPLIANT - printf("% f", 42); // COMPLIANT - printf("%#f", 42); // COMPLIANT - printf("%0f", 42); // COMPLIANT - - printf("%'F", 42); // COMPLIANT - printf("%-F", 42); // COMPLIANT - printf("%+F", 42); // COMPLIANT - printf("% F", 42); // COMPLIANT - printf("%#F", 42); // COMPLIANT - printf("%0F", 42); // COMPLIANT - - printf("%'e", 42); // NON_COMPLIANT - printf("%-e", 42); // COMPLIANT - printf("%+e", 42); // COMPLIANT - printf("% e", 42); // COMPLIANT - printf("%#e", 42); // COMPLIANT - printf("%0e", 42); // COMPLIANT - - printf("%'E", 42); // NON_COMPLIANT - printf("%-E", 42); // COMPLIANT - printf("%+E", 42); // COMPLIANT - printf("% E", 42); // COMPLIANT - printf("%#E", 42); // COMPLIANT - printf("%0E", 42); // COMPLIANT - - printf("%'g", 42); // COMPLIANT - printf("%-g", 42); // COMPLIANT - printf("%+g", 42); // COMPLIANT - printf("% g", 42); // COMPLIANT - printf("%#g", 42); // COMPLIANT - printf("%0g", 42); // COMPLIANT - - printf("%'G", 42); // COMPLIANT - printf("%-G", 42); // COMPLIANT - printf("%+G", 42); // COMPLIANT - printf("% G", 42); // COMPLIANT - printf("%#G", 42); // COMPLIANT - printf("%0G", 42); // COMPLIANT - - printf("%'a", 42); // COMPLIANT - printf("%-a", 42); // COMPLIANT - printf("%+a", 42); // COMPLIANT - printf("% a", 42); // COMPLIANT - printf("%#a", 42); // COMPLIANT - printf("%0a", 42); // COMPLIANT - - printf("%'A", 42); // COMPLIANT - printf("%-A", 42); // COMPLIANT - printf("%+A", 42); // COMPLIANT - printf("% A", 42); // COMPLIANT - printf("%#A", 42); // COMPLIANT - printf("%0A", 42); // COMPLIANT - - printf("%'c", 42); // NON_COMPLIANT - printf("%-c", 42); // COMPLIANT - printf("%+c", 42); // COMPLIANT - printf("% c", 42); // COMPLIANT - printf("%#c", 42); // NON_COMPLIANT - printf("%0c", 42); // NON_COMPLIANT - - printf("%'s", 42); // NON_COMPLIANT - printf("%-s", 42); // COMPLIANT - printf("%+s", 42); // COMPLIANT - printf("% s", 42); // COMPLIANT - printf("%#s", 42); // NON_COMPLIANT - printf("%0s", 42); // NON_COMPLIANT - - printf("%'p", 42); // NON_COMPLIANT - printf("%-p", 42); // COMPLIANT - printf("%+p", 42); // COMPLIANT - printf("% p", 42); // COMPLIANT - printf("%#p", 42); // NON_COMPLIANT - printf("%0p", 42); // NON_COMPLIANT - - printf("%'n", 42); // NON_COMPLIANT - printf("%-n", 42); // COMPLIANT - printf("%+n", 42); // COMPLIANT - printf("% n", 42); // COMPLIANT - printf("%#n", 42); // NON_COMPLIANT - printf("%0n", 42); // NON_COMPLIANT - - printf("%'C", 42); // NON_COMPLIANT - printf("%-C", 42); // COMPLIANT - printf("%+C", 42); // COMPLIANT - printf("% C", 42); // COMPLIANT - printf("%#C", 42); // NON_COMPLIANT - printf("%0C", 42); // NON_COMPLIANT - - printf("%'S", 42); // NON_COMPLIANT - printf("%-S", 42); // COMPLIANT - printf("%+S", 42); // COMPLIANT - printf("% S", 42); // COMPLIANT - printf("%#S", 42); // NON_COMPLIANT - printf("%0S", 42); // NON_COMPLIANT - - printf("%%", 42); // COMPLIANT - printf("%'%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%-%", 42); // COMPLIANT - printf("%+%", 42); // COMPLIANT - printf("% %", 42); // COMPLIANT - printf("%#%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%0%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%'-d", v_signed_int); // COMPLIANT + printf("%'-#d", v_signed_int); // NON_COMPLIANT + + printf("%'d", v_signed_int); // COMPLIANT + printf("%-d", v_signed_int); // COMPLIANT + printf("%+d", v_signed_int); // COMPLIANT + printf("% d", v_signed_int); // COMPLIANT + printf("%#d", v_signed_int); // NON_COMPLIANT + printf("%0d", v_signed_int); // COMPLIANT + + printf("%'i", v_signed_int); // COMPLIANT + printf("%-i", v_signed_int); // COMPLIANT + printf("%+i", v_signed_int); // COMPLIANT + printf("% i", v_signed_int); // COMPLIANT + printf("%#i", v_signed_int); // NON_COMPLIANT + printf("%0i", v_signed_int); // COMPLIANT + + printf("%'o", v_unsigned_int); // NON_COMPLIANT + printf("%-o", v_unsigned_int); // COMPLIANT + printf("%+o", v_unsigned_int); // COMPLIANT + printf("% o", v_unsigned_int); // COMPLIANT + printf("%#o", v_unsigned_int); // COMPLIANT + printf("%0o", v_unsigned_int); // COMPLIANT + + printf("%'u", v_unsigned_int); // COMPLIANT + printf("%-u", v_unsigned_int); // COMPLIANT + printf("%+u", v_unsigned_int); // COMPLIANT + printf("% u", v_unsigned_int); // COMPLIANT + printf("%#u", v_unsigned_int); // NON_COMPLIANT + printf("%0u", v_unsigned_int); // COMPLIANT + + printf("%'x", v_unsigned_int); // NON_COMPLIANT + printf("%-x", v_unsigned_int); // COMPLIANT + printf("%+x", v_unsigned_int); // COMPLIANT + printf("% x", v_unsigned_int); // COMPLIANT + printf("%#x", v_unsigned_int); // COMPLIANT + printf("%0x", v_unsigned_int); // COMPLIANT + + printf("%'X", v_unsigned_int); // NON_COMPLIANT + printf("%-X", v_unsigned_int); // COMPLIANT + printf("%+X", v_unsigned_int); // COMPLIANT + printf("% X", v_unsigned_int); // COMPLIANT + printf("%#X", v_unsigned_int); // COMPLIANT + printf("%0X", v_unsigned_int); // COMPLIANT + + printf("%'f", v_double); // COMPLIANT + printf("%-f", v_double); // COMPLIANT + printf("%+f", v_double); // COMPLIANT + printf("% f", v_double); // COMPLIANT + printf("%#f", v_double); // COMPLIANT + printf("%0f", v_double); // COMPLIANT + + printf("%'F", v_double); // COMPLIANT + printf("%-F", v_double); // COMPLIANT + printf("%+F", v_double); // COMPLIANT + printf("% F", v_double); // COMPLIANT + printf("%#F", v_double); // COMPLIANT + printf("%0F", v_double); // COMPLIANT + + printf("%'e", v_double); // NON_COMPLIANT + printf("%-e", v_double); // COMPLIANT + printf("%+e", v_double); // COMPLIANT + printf("% e", v_double); // COMPLIANT + printf("%#e", v_double); // COMPLIANT + printf("%0e", v_double); // COMPLIANT + + printf("%'E", v_double); // NON_COMPLIANT + printf("%-E", v_double); // COMPLIANT + printf("%+E", v_double); // COMPLIANT + printf("% E", v_double); // COMPLIANT + printf("%#E", v_double); // COMPLIANT + printf("%0E", v_double); // COMPLIANT + + printf("%'g", v_double); // COMPLIANT + printf("%-g", v_double); // COMPLIANT + printf("%+g", v_double); // COMPLIANT + printf("% g", v_double); // COMPLIANT + printf("%#g", v_double); // COMPLIANT + printf("%0g", v_double); // COMPLIANT + + printf("%'G", v_double); // COMPLIANT + printf("%-G", v_double); // COMPLIANT + printf("%+G", v_double); // COMPLIANT + printf("% G", v_double); // COMPLIANT + printf("%#G", v_double); // COMPLIANT + printf("%0G", v_double); // COMPLIANT + + printf("%'a", v_double); // COMPLIANT + printf("%-a", v_double); // COMPLIANT + printf("%+a", v_double); // COMPLIANT + printf("% a", v_double); // COMPLIANT + printf("%#a", v_double); // COMPLIANT + printf("%0a", v_double); // COMPLIANT + + printf("%'A", v_double); // COMPLIANT + printf("%-A", v_double); // COMPLIANT + printf("%+A", v_double); // COMPLIANT + printf("% A", v_double); // COMPLIANT + printf("%#A", v_double); // COMPLIANT + printf("%0A", v_double); // COMPLIANT + + printf("%'c", v_int); // NON_COMPLIANT + printf("%-c", v_int); // COMPLIANT + printf("%+c", v_int); // COMPLIANT + printf("% c", v_int); // COMPLIANT + printf("%#c", v_int); // NON_COMPLIANT + printf("%0c", v_int); // NON_COMPLIANT + + printf("%'s", v_char_ptr); // NON_COMPLIANT + printf("%-s", v_char_ptr); // COMPLIANT + printf("%+s", v_char_ptr); // COMPLIANT + printf("% s", v_char_ptr); // COMPLIANT + printf("%#s", v_char_ptr); // NON_COMPLIANT + printf("%0s", v_char_ptr); // NON_COMPLIANT + + printf("%'p", v_void_ptr); // NON_COMPLIANT + printf("%-p", v_void_ptr); // COMPLIANT + printf("%+p", v_void_ptr); // COMPLIANT + printf("% p", v_void_ptr); // COMPLIANT + printf("%#p", v_void_ptr); // NON_COMPLIANT + printf("%0p", v_void_ptr); // NON_COMPLIANT + + printf("%'n", v_int_ptr); // NON_COMPLIANT + printf("%-n", v_int_ptr); // NON_COMPLIANT + printf("%+n", v_int_ptr); // NON_COMPLIANT + printf("% n", v_int_ptr); // NON_COMPLIANT + printf("%#n", v_int_ptr); // NON_COMPLIANT + printf("%0n", v_int_ptr); // NON_COMPLIANT + + printf("%'C", v_wint_t); // NON_COMPLIANT + printf("%-C", v_wint_t); // COMPLIANT + printf("%+C", v_wint_t); // COMPLIANT + printf("% C", v_wint_t); // COMPLIANT + printf("%#C", v_wint_t); // NON_COMPLIANT + printf("%0C", v_wint_t); // NON_COMPLIANT + + printf("%'S", v_wchar_t_ptr); // NON_COMPLIANT + printf("%-S", v_wchar_t_ptr); // COMPLIANT + printf("%+S", v_wchar_t_ptr); // COMPLIANT + printf("% S", v_wchar_t_ptr); // COMPLIANT + printf("%#S", v_wchar_t_ptr); // NON_COMPLIANT + printf("%0S", v_wchar_t_ptr); // NON_COMPLIANT + + printf("%%"); // COMPLIANT + printf("%'%"); // NON_COMPLIANT + printf("%-%"); // NON_COMPLIANT + printf("%+%"); // NON_COMPLIANT + printf("% %"); // NON_COMPLIANT + printf("%#%"); // NON_COMPLIANT + printf("%0%"); // NON_COMPLIANT + printf("%0%"); // NON_COMPLIANT } + void test_incompatible_length() { - printf("%hd", 42); // COMPLIANT - printf("%hhd", 42); // COMPLIANT - printf("%ld", 42); // COMPLIANT - printf("%lld", 42); // COMPLIANT - printf("%jd", 42); // COMPLIANT - printf("%zd", 42); // COMPLIANT - printf("%td", 42); // COMPLIANT - printf("%Ld", 42); // NON_COMPLIANT - - printf("%hi", 42); // COMPLIANT - printf("%hhi", 42); // COMPLIANT - printf("%li", 42); // COMPLIANT - printf("%lli", 42); // COMPLIANT - printf("%ji", 42); // COMPLIANT - printf("%zi", 42); // COMPLIANT - printf("%ti", 42); // COMPLIANT - printf("%Li", 42); // NON_COMPLIANT - - printf("%ho", 42); // COMPLIANT - printf("%hho", 42); // COMPLIANT - printf("%lo", 42); // COMPLIANT - printf("%llo", 42); // COMPLIANT - printf("%jo", 42); // COMPLIANT - printf("%zo", 42); // COMPLIANT - printf("%to", 42); // COMPLIANT - printf("%Lo", 42); // NON_COMPLIANT - - printf("%hu", 42); // COMPLIANT - printf("%hhu", 42); // COMPLIANT - printf("%lu", 42); // COMPLIANT - printf("%llu", 42); // COMPLIANT - printf("%ju", 42); // COMPLIANT - printf("%zu", 42); // COMPLIANT - printf("%tu", 42); // COMPLIANT - printf("%Lu", 42); // NON_COMPLIANT - - printf("%hx", 42); // NON_COMPLIANT - printf("%hhx", 42); // NON_COMPLIANT - printf("%lx", 42); // COMPLIANT - printf("%llx", 42); // COMPLIANT - printf("%jx", 42); // NON_COMPLIANT - printf("%zx", 42); // NON_COMPLIANT - printf("%tx", 42); // NON_COMPLIANT - printf("%Lx", 42); // NON_COMPLIANT - - printf("%hX", 42); // NON_COMPLIANT - printf("%hhX", 42); // NON_COMPLIANT - printf("%lX", 42); // COMPLIANT - printf("%llX", 42); // COMPLIANT - printf("%jX", 42); // NON_COMPLIANT - printf("%zX", 42); // NON_COMPLIANT - printf("%tX", 42); // NON_COMPLIANT - printf("%LX", 42); // NON_COMPLIANT - - printf("%hf", 42); // NON_COMPLIANT - printf("%hhf", 42); // NON_COMPLIANT - printf("%lf", 42); // COMPLIANT - printf("%llf", 42); // COMPLIANT - printf("%jf", 42); // NON_COMPLIANT - printf("%zf", 42); // NON_COMPLIANT - printf("%tf", 42); // NON_COMPLIANT - printf("%Lf", 42); // COMPLIANT - - printf("%hF", 42); // NON_COMPLIANT - printf("%hhF", 42); // NON_COMPLIANT - printf("%lF", 42); // COMPLIANT - printf("%llF", 42); // COMPLIANT - printf("%jF", 42); // NON_COMPLIANT - printf("%zF", 42); // NON_COMPLIANT - printf("%tF", 42); // NON_COMPLIANT - printf("%LF", 42); // COMPLIANT - - printf("%he", 42); // NON_COMPLIANT - printf("%hhe", 42); // NON_COMPLIANT - printf("%le", 42); // COMPLIANT - printf("%lle", 42); // COMPLIANT - printf("%je", 42); // NON_COMPLIANT - printf("%ze", 42); // NON_COMPLIANT - printf("%te", 42); // NON_COMPLIANT - printf("%Le", 42); // COMPLIANT - - printf("%hE", 42); // NON_COMPLIANT - printf("%hhE", 42); // NON_COMPLIANT - printf("%lE", 42); // COMPLIANT - printf("%llE", 42); // COMPLIANT - printf("%jE", 42); // NON_COMPLIANT - printf("%zE", 42); // NON_COMPLIANT - printf("%tE", 42); // NON_COMPLIANT - printf("%LE", 42); // COMPLIANT - - printf("%hg", 42); // NON_COMPLIANT - printf("%hhg", 42); // NON_COMPLIANT - printf("%lg", 42); // COMPLIANT - printf("%llg", 42); // COMPLIANT - printf("%jg", 42); // NON_COMPLIANT - printf("%zg", 42); // NON_COMPLIANT - printf("%tg", 42); // NON_COMPLIANT - printf("%Lg", 42); // COMPLIANT - - printf("%hG", 42); // NON_COMPLIANT - printf("%hhG", 42); // NON_COMPLIANT - printf("%lG", 42); // COMPLIANT - printf("%llG", 42); // COMPLIANT - printf("%jG", 42); // NON_COMPLIANT - printf("%zG", 42); // NON_COMPLIANT - printf("%tG", 42); // NON_COMPLIANT - printf("%LG", 42); // COMPLIANT - - printf("%ha", 42); // NON_COMPLIANT - printf("%hha", 42); // NON_COMPLIANT - printf("%la", 42); // COMPLIANT - printf("%lla", 42); // COMPLIANT - printf("%ja", 42); // NON_COMPLIANT - printf("%za", 42); // NON_COMPLIANT - printf("%ta", 42); // NON_COMPLIANT - printf("%La", 42); // COMPLIANT - - printf("%hA", 42); // NON_COMPLIANT - printf("%hhA", 42); // NON_COMPLIANT - printf("%lA", 42); // COMPLIANT - printf("%llA", 42); // COMPLIANT - printf("%jA", 42); // NON_COMPLIANT - printf("%zA", 42); // NON_COMPLIANT - printf("%tA", 42); // NON_COMPLIANT - printf("%LA", 42); // COMPLIANT - - printf("%hc", 42); // NON_COMPLIANT - printf("%hhc", 42); // NON_COMPLIANT - printf("%lc", 42); // COMPLIANT - printf("%llc", 42); // NON_COMPLIANT - printf("%jc", 42); // NON_COMPLIANT - printf("%zc", 42); // NON_COMPLIANT - printf("%tc", 42); // NON_COMPLIANT - printf("%Lc", 42); // NON_COMPLIANT - - printf("%hs", 42); // NON_COMPLIANT - printf("%hhs", 42); // NON_COMPLIANT - printf("%ls", 42); // COMPLIANT - printf("%lls", 42); // NON_COMPLIANT - printf("%js", 42); // NON_COMPLIANT - printf("%zs", 42); // NON_COMPLIANT - printf("%ts", 42); // NON_COMPLIANT - printf("%Ls", 42); // NON_COMPLIANT - - printf("%hp", 42); // NON_COMPLIANT - printf("%hhp", 42); // NON_COMPLIANT - printf("%lp", 42); // NON_COMPLIANT - printf("%llp", 42); // NON_COMPLIANT - printf("%jp", 42); // NON_COMPLIANT - printf("%zp", 42); // NON_COMPLIANT - printf("%tp", 42); // NON_COMPLIANT - printf("%Lp", 42); // NON_COMPLIANT - - printf("%hn", 42); // COMPLIANT - printf("%hhn", 42); // COMPLIANT - printf("%ln", 42); // COMPLIANT - printf("%lln", 42); // COMPLIANT - printf("%jn", 42); // COMPLIANT - printf("%zn", 42); // COMPLIANT - printf("%tn", 42); // COMPLIANT - printf("%Ln", 42); // NON_COMPLIANT - - printf("%hC", 42); // NON_COMPLIANT - printf("%hhC", 42); // NON_COMPLIANT - printf("%lC", 42); // NON_COMPLIANT - printf("%llC", 42); // NON_COMPLIANT - printf("%jC", 42); // NON_COMPLIANT - printf("%zC", 42); // NON_COMPLIANT - printf("%tC", 42); // NON_COMPLIANT - printf("%LC", 42); // NON_COMPLIANT - - printf("%hS", 42); // NON_COMPLIANT - printf("%hhS", 42); // NON_COMPLIANT - printf("%lS", 42); // NON_COMPLIANT - printf("%llS", 42); // NON_COMPLIANT - printf("%jS", 42); // NON_COMPLIANT - printf("%zS", 42); // NON_COMPLIANT - printf("%tS", 42); // NON_COMPLIANT - printf("%LS", 42); // NON_COMPLIANT - - printf("%h%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%hh%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%l%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%ll%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%j%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%z%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%t%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%L%", 42); // NON_COMPLIANT[FALSE_NEGATIVE] -} -void test_wrong_width_type() { - printf("%*d", 2, 42); // COMPLIANT - printf("%*d", "2", 42); // NON_COMPLIANT -} -void test_wrong_precision_type() { - printf("%.*d", 2, 42); // COMPLIANT - printf("%.*d", "2", 42); // NON_COMPLIANT + printf("%hd", v_short); // COMPLIANT + printf("%hhd", v_signed_char); // COMPLIANT + printf("%ld", v_long); // COMPLIANT + printf("%lld", v_long_long); // COMPLIANT + printf("%jd", v_intmax_t); // COMPLIANT + printf("%zd", v_size_t); // COMPLIANT + printf("%td", v_ptrdiff_t); // COMPLIANT + printf("%Ld", v_long_long); // NON_COMPLIANT + + printf("%hi", v_short); // COMPLIANT + printf("%hhi", v_signed_char); // COMPLIANT + printf("%li", v_long); // COMPLIANT + printf("%lli", v_long_long); // COMPLIANT + printf("%ji", v_intmax_t); // COMPLIANT + printf("%zi", v_size_t); // COMPLIANT + printf("%ti", v_ptrdiff_t); // COMPLIANT + printf("%Li", v_long_long); // NON_COMPLIANT + + printf("%ho", v_unsigned_short); // COMPLIANT + printf("%hho", v_unsigned_char); // COMPLIANT + printf("%lo", v_unsigned_long); // COMPLIANT + printf("%llo", v_unsigned_long_long); // COMPLIANT + printf("%jo", v_uintmax_t); // COMPLIANT + printf("%zo", v_size_t); // COMPLIANT + printf("%to", v_ptrdiff_t); // COMPLIANT + printf("%Lo", v_unsigned_long_long); // NON_COMPLIANT + + printf("%hu", v_unsigned_short); // COMPLIANT + printf("%hhu", v_unsigned_char); // COMPLIANT + printf("%lu", v_unsigned_long); // COMPLIANT + printf("%llu", v_unsigned_long_long); // COMPLIANT + printf("%ju", v_uintmax_t); // COMPLIANT + printf("%zu", v_size_t); // COMPLIANT + printf("%tu", v_ptrdiff_t); // COMPLIANT + printf("%Lu", v_unsigned_long_long); // NON_COMPLIANT + + printf("%hx", v_unsigned_short); // COMPLIANT + printf("%hhx", v_unsigned_char); // COMPLIANT + printf("%lx", v_unsigned_long); // COMPLIANT + printf("%llx", v_unsigned_long_long); // COMPLIANT + printf("%jx", v_uintmax_t); // COMPLIANT + printf("%zx", v_size_t); // COMPLIANT + printf("%tx", v_ptrdiff_t); // COMPLIANT + printf("%Lx", v_unsigned_long_long); // NON_COMPLIANT + + printf("%hX", v_unsigned_short); // COMPLIANT + printf("%hhX", v_unsigned_char); // COMPLIANT + printf("%lX", v_unsigned_long); // COMPLIANT + printf("%llX", v_unsigned_long_long); // COMPLIANT + printf("%jX", v_uintmax_t); // COMPLIANT + printf("%zX", v_size_t); // COMPLIANT + printf("%tX", v_ptrdiff_t); // COMPLIANT + printf("%LX", v_unsigned_long_long); // NON_COMPLIANT + + printf("%hf", v_double); // NON_COMPLIANT + printf("%hhf", v_double); // NON_COMPLIANT + printf("%lf", v_double); // COMPLIANT + printf("%llf", v_long_double); // COMPLIANT + printf("%jf", v_double); // NON_COMPLIANT + printf("%zf", v_double); // NON_COMPLIANT + printf("%tf", v_double); // NON_COMPLIANT + printf("%Lf", v_long_double); // COMPLIANT + + printf("%hF", v_double); // NON_COMPLIANT + printf("%hhF", v_double); // NON_COMPLIANT + printf("%lF", v_double); // COMPLIANT + printf("%llF", v_long_double); // COMPLIANT + printf("%jF", v_double); // NON_COMPLIANT + printf("%zF", v_double); // NON_COMPLIANT + printf("%tF", v_double); // NON_COMPLIANT + printf("%LF", v_long_double); // COMPLIANT + + printf("%he", v_double); // NON_COMPLIANT + printf("%hhe", v_double); // NON_COMPLIANT + printf("%le", v_double); // COMPLIANT + printf("%lle", v_long_double); // COMPLIANT + printf("%je", v_double); // NON_COMPLIANT + printf("%ze", v_double); // NON_COMPLIANT + printf("%te", v_double); // NON_COMPLIANT + printf("%Le", v_long_double); // COMPLIANT + + printf("%hE", v_double); // NON_COMPLIANT + printf("%hhE", v_double); // NON_COMPLIANT + printf("%lE", v_double); // COMPLIANT + printf("%llE", v_long_double); // COMPLIANT + printf("%jE", v_double); // NON_COMPLIANT + printf("%zE", v_double); // NON_COMPLIANT + printf("%tE", v_double); // NON_COMPLIANT + printf("%LE", v_long_double); // COMPLIANT + + printf("%hg", v_double); // NON_COMPLIANT + printf("%hhg", v_double); // NON_COMPLIANT + printf("%lg", v_double); // COMPLIANT + printf("%llg", v_long_double); // COMPLIANT + printf("%jg", v_double); // NON_COMPLIANT + printf("%zg", v_double); // NON_COMPLIANT + printf("%tg", v_double); // NON_COMPLIANT + printf("%Lg", v_long_double); // COMPLIANT + + printf("%hG", v_double); // NON_COMPLIANT + printf("%hhG", v_double); // NON_COMPLIANT + printf("%lG", v_double); // COMPLIANT + printf("%llG", v_long_double); // COMPLIANT + printf("%jG", v_double); // NON_COMPLIANT + printf("%zG", v_double); // NON_COMPLIANT + printf("%tG", v_double); // NON_COMPLIANT + printf("%LG", v_long_double); // COMPLIANT + + printf("%ha", v_double); // NON_COMPLIANT + printf("%hha", v_double); // NON_COMPLIANT + printf("%la", v_double); // COMPLIANT + printf("%lla", v_long_double); // COMPLIANT + printf("%ja", v_double); // NON_COMPLIANT + printf("%za", v_double); // NON_COMPLIANT + printf("%ta", v_double); // NON_COMPLIANT + printf("%La", v_long_double); // COMPLIANT + + printf("%hA", v_double); // NON_COMPLIANT + printf("%hhA", v_double); // NON_COMPLIANT + printf("%lA", v_double); // COMPLIANT + printf("%llA", v_long_double); // COMPLIANT + printf("%jA", v_double); // NON_COMPLIANT + printf("%zA", v_double); // NON_COMPLIANT + printf("%tA", v_double); // NON_COMPLIANT + printf("%LA", v_long_double); // COMPLIANT + + printf("%hc", v_int); // NON_COMPLIANT + printf("%hhc", v_int); // NON_COMPLIANT + printf("%lc", v_wint_t); // COMPLIANT + printf("%llc", v_int); // NON_COMPLIANT + printf("%jc", v_int); // NON_COMPLIANT + printf("%zc", v_int); // NON_COMPLIANT + printf("%tc", v_int); // NON_COMPLIANT + printf("%Lc", v_int); // NON_COMPLIANT + + printf("%hs", v_char_ptr); // NON_COMPLIANT + printf("%hhs", v_char_ptr); // NON_COMPLIANT + printf("%ls", v_wchar_t_ptr); // COMPLIANT + printf("%lls", v_char_ptr); // NON_COMPLIANT + printf("%js", v_char_ptr); // NON_COMPLIANT + printf("%zs", v_char_ptr); // NON_COMPLIANT + printf("%ts", v_char_ptr); // NON_COMPLIANT + printf("%Ls", v_char_ptr); // NON_COMPLIANT + + printf("%hp", v_void_ptr); // NON_COMPLIANT + printf("%hhp", v_void_ptr); // NON_COMPLIANT + printf("%lp", v_void_ptr); // NON_COMPLIANT + printf("%llp", v_void_ptr); // NON_COMPLIANT + printf("%jp", v_void_ptr); // NON_COMPLIANT + printf("%zp", v_void_ptr); // NON_COMPLIANT + printf("%tp", v_void_ptr); // NON_COMPLIANT + printf("%Lp", v_void_ptr); // NON_COMPLIANT + + printf("%hn", v_short_ptr); // COMPLIANT + printf("%hhn", v_char_ptr); // COMPLIANT + printf("%ln", v_long_ptr); // COMPLIANT + printf("%lln", v_long_long_ptr); // COMPLIANT + printf("%jn", v_intmax_t_ptr); // COMPLIANT[FALSE_POSITIVE] + printf("%zn", v_size_t_ptr); // COMPLIANT[FALSE_POSITIVE] + printf("%tn", v_ptrdiff_t_ptr); // COMPLIANT[FALSE_POSITIVE] + printf("%Ln", v_long_long_ptr); // NON_COMPLIANT + + printf("%hC", v_wint_t); // NON_COMPLIANT + printf("%hhC", v_wint_t); // NON_COMPLIANT + printf("%lC", v_wint_t); // NON_COMPLIANT + printf("%llC", v_wint_t); // NON_COMPLIANT + printf("%jC", v_wint_t); // NON_COMPLIANT + printf("%zC", v_wint_t); // NON_COMPLIANT + printf("%tC", v_wint_t); // NON_COMPLIANT + printf("%LC", v_wint_t); // NON_COMPLIANT + + printf("%hS", v_char_ptr); // NON_COMPLIANT + printf("%hhS", v_wchar_t_ptr); // NON_COMPLIANT + printf("%lS", v_wchar_t_ptr); // NON_COMPLIANT + printf("%llS", v_wchar_t_ptr); // NON_COMPLIANT + printf("%jS", v_wchar_t_ptr); // NON_COMPLIANT + printf("%zS", v_wchar_t_ptr); // NON_COMPLIANT + printf("%tS", v_wchar_t_ptr); // NON_COMPLIANT + printf("%LS", v_wchar_t_ptr); // NON_COMPLIANT + + printf("%h%"); // NON_COMPLIANT + printf("%hh%"); // NON_COMPLIANT + printf("%l%"); // NON_COMPLIANT + printf("%ll%"); // NON_COMPLIANT + printf("%j%"); // NON_COMPLIANT + printf("%z%"); // NON_COMPLIANT + printf("%t%"); // NON_COMPLIANT + printf("%L%"); // NON_COMPLIANT } void test_incompatible_conversion() { printf(""); // COMPLIANT printf(""); // NON_COMPLIANT } void test_wrong_arg_type() { - printf("%d", 42); // COMPLIANT - printf("%s", 42); // NON_COMPLIANT + // wrong width type + printf("%*d", 2, 42); // COMPLIANT + printf("%*d", v_char_ptr, 42); // NON_COMPLIANT + + // wrong precision type + printf("%.*d", 2, 42); // COMPLIANT + printf("%.*d", v_char_ptr, 42); // NON_COMPLIANT + + // compliant cases are in test_incompatible_flag() and + // test_incompatible_length() + printf("%d", v_char_ptr); // NON_COMPLIANT + printf("%hd", v_char_ptr); // NON_COMPLIANT + printf("%hhd", v_char_ptr); // NON_COMPLIANT + printf("%ld", v_char_ptr); // NON_COMPLIANT + printf("%lld", v_char_ptr); // NON_COMPLIANT + printf("%jd", v_char_ptr); // NON_COMPLIANT + printf("%zd", v_char_ptr); // NON_COMPLIANT + printf("%td", v_char_ptr); // NON_COMPLIANT + + printf("%i", v_char_ptr); // NON_COMPLIANT + printf("%hi", v_char_ptr); // NON_COMPLIANT + printf("%hhi", v_char_ptr); // NON_COMPLIANT + printf("%li", v_char_ptr); // NON_COMPLIANT + printf("%lli", v_char_ptr); // NON_COMPLIANT + printf("%ji", v_char_ptr); // NON_COMPLIANT + printf("%zi", v_char_ptr); // NON_COMPLIANT + printf("%ti", v_char_ptr); // NON_COMPLIANT + + printf("%o", v_char_ptr); // NON_COMPLIANT + printf("%ho", v_char_ptr); // NON_COMPLIANT + printf("%hho", v_char_ptr); // NON_COMPLIANT + printf("%lo", v_char_ptr); // NON_COMPLIANT + printf("%llo", v_char_ptr); // NON_COMPLIANT + printf("%jo", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%zo", v_char_ptr); // NON_COMPLIANT + printf("%to", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + + printf("%u", v_char_ptr); // NON_COMPLIANT + printf("%hu", v_char_ptr); // NON_COMPLIANT + printf("%hhu", v_char_ptr); // NON_COMPLIANT + printf("%lu", v_char_ptr); // NON_COMPLIANT + printf("%llu", v_char_ptr); // NON_COMPLIANT + printf("%ju", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%zu", v_char_ptr); // NON_COMPLIANT + printf("%tu", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + + printf("%x", v_char_ptr); // NON_COMPLIANT + printf("%hx", v_char_ptr); // NON_COMPLIANT + printf("%hhx", v_char_ptr); // NON_COMPLIANT + printf("%lx", v_char_ptr); // NON_COMPLIANT + printf("%llx", v_char_ptr); // NON_COMPLIANT + printf("%jx", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%zx", v_char_ptr); // NON_COMPLIANT + printf("%tx", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + + printf("%X", v_char_ptr); // NON_COMPLIANT + printf("%hX", v_char_ptr); // NON_COMPLIANT + printf("%hhX", v_char_ptr); // NON_COMPLIANT + printf("%lX", v_char_ptr); // NON_COMPLIANT + printf("%llX", v_char_ptr); // NON_COMPLIANT + printf("%jX", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%zX", v_char_ptr); // NON_COMPLIANT + printf("%tX", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + + printf("%f", v_char_ptr); // NON_COMPLIANT + printf("%lf", v_char_ptr); // NON_COMPLIANT + printf("%llf", v_char_ptr); // NON_COMPLIANT + printf("%Lf", v_char_ptr); // NON_COMPLIANT + + printf("%F", v_char_ptr); // NON_COMPLIANT + printf("%lF", v_char_ptr); // NON_COMPLIANT + printf("%llF", v_char_ptr); // NON_COMPLIANT + printf("%LF", v_char_ptr); // NON_COMPLIANT + + printf("%e", v_char_ptr); // NON_COMPLIANT + printf("%le", v_char_ptr); // NON_COMPLIANT + printf("%lle", v_char_ptr); // NON_COMPLIANT + printf("%Le", v_char_ptr); // NON_COMPLIANT + + printf("%E", v_char_ptr); // NON_COMPLIANT + printf("%lE", v_char_ptr); // NON_COMPLIANT + printf("%llE", v_char_ptr); // NON_COMPLIANT + printf("%LE", v_char_ptr); // NON_COMPLIANT + + printf("%g", v_char_ptr); // NON_COMPLIANT + printf("%lg", v_char_ptr); // NON_COMPLIANT + printf("%llg", v_char_ptr); // NON_COMPLIANT + printf("%Lg", v_char_ptr); // NON_COMPLIANT + + printf("%G", v_char_ptr); // NON_COMPLIANT + printf("%lG", v_char_ptr); // NON_COMPLIANT + printf("%llG", v_char_ptr); // NON_COMPLIANT + printf("%LG", v_char_ptr); // NON_COMPLIANT + + printf("%a", v_char_ptr); // NON_COMPLIANT + printf("%la", v_char_ptr); // NON_COMPLIANT + printf("%lla", v_char_ptr); // NON_COMPLIANT + printf("%La", v_char_ptr); // NON_COMPLIANT + + printf("%A", v_char_ptr); // NON_COMPLIANT + printf("%lA", v_char_ptr); // NON_COMPLIANT + printf("%llA", v_char_ptr); // NON_COMPLIANT + printf("%LA", v_char_ptr); // NON_COMPLIANT + + printf("%c", v_char_ptr); // NON_COMPLIANT + printf("%lc", v_char_ptr); // NON_COMPLIANT + + printf("%s", v_int); // NON_COMPLIANT + printf("%ls", v_int); // NON_COMPLIANT + + printf("%p", v_int); // NON_COMPLIANT + + printf("%n", v_int); // NON_COMPLIANT + printf("%hn", v_int); // NON_COMPLIANT + printf("%hhn", v_int); // NON_COMPLIANT + printf("%ln", v_int); // NON_COMPLIANT + printf("%lln", v_int); // NON_COMPLIANT + printf("%jn", v_int); // NON_COMPLIANT + printf("%zn", v_int); // NON_COMPLIANT + printf("%tn", v_int); // NON_COMPLIANT + + printf("%C", v_char_ptr); // NON_COMPLIANT + + printf("%S", v_int); // NON_COMPLIANT } + void test_wrong_arg_number() { printf("%d", 42); // COMPLIANT printf("%d, %s\n", 42); // NON_COMPLIANT diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll index 1b01fe2371..a1c3978fc4 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll @@ -7,10 +7,7 @@ newtype IO4Query = TToctouRaceConditionsWhileAccessingFilesQuery() or TUseValidSpecifiersQuery() or TWrongNumberOfFormatArgumentsQuery() or - TWrongTypeFormatArgumentsQuery() or - TUseValidSpecifierFlagsQuery() or - TUseValidSpecifierLengthQuery() or - TUseIntArgumentForWidthAndPrecisionQuery() + TWrongTypeFormatArgumentsQuery() predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { query = @@ -44,30 +41,6 @@ predicate isIO4QueryMetadata(Query query, string queryId, string ruleId) { // `@id` for the `wrongTypeFormatArguments` query "c/cert/wrong-type-format-arguments" and ruleId = "FIO47-C" - or - query = - // `Query` instance for the `useValidSpecifierFlags` query - IO4Package::useValidSpecifierFlagsQuery() and - queryId = - // `@id` for the `useValidSpecifierFlags` query - "c/cert/use-valid-specifier-flags" and - ruleId = "FIO47-C" - or - query = - // `Query` instance for the `useValidSpecifierLength` query - IO4Package::useValidSpecifierLengthQuery() and - queryId = - // `@id` for the `useValidSpecifierLength` query - "c/cert/use-valid-specifier-length" and - ruleId = "FIO47-C" - or - query = - // `Query` instance for the `useIntArgumentForWidthAndPrecision` query - IO4Package::useIntArgumentForWidthAndPrecisionQuery() and - queryId = - // `@id` for the `useIntArgumentForWidthAndPrecision` query - "c/cert/use-int-argument-for-width-and-precision" and - ruleId = "FIO47-C" } module IO4Package { @@ -98,25 +71,4 @@ module IO4Package { // `Query` type for `wrongTypeFormatArguments` query TQueryC(TIO4PackageQuery(TWrongTypeFormatArgumentsQuery())) } - - Query useValidSpecifierFlagsQuery() { - //autogenerate `Query` type - result = - // `Query` type for `useValidSpecifierFlags` query - TQueryC(TIO4PackageQuery(TUseValidSpecifierFlagsQuery())) - } - - Query useValidSpecifierLengthQuery() { - //autogenerate `Query` type - result = - // `Query` type for `useValidSpecifierLength` query - TQueryC(TIO4PackageQuery(TUseValidSpecifierLengthQuery())) - } - - Query useIntArgumentForWidthAndPrecisionQuery() { - //autogenerate `Query` type - result = - // `Query` type for `useIntArgumentForWidthAndPrecision` query - TQueryC(TIO4PackageQuery(TUseIntArgumentForWidthAndPrecisionQuery())) - } } diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 3daf836e20..bf92db99b8 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -63,42 +63,6 @@ "correctness", "security" ] - }, - { - "description": "Use valid specifier flags", - "kind": "problem", - "name": "Use valid specifier flags", - "precision": "high", - "severity": "error", - "short_name": "UseValidSpecifierFlags", - "tags": [ - "correctness", - "security" - ] - }, - { - "description": "Use valid specifier length", - "kind": "problem", - "name": "Use valid specifier length", - "precision": "high", - "severity": "error", - "short_name": "UseValidSpecifierLength", - "tags": [ - "correctness", - "security" - ] - }, - { - "description": "Use `int` arguments for `width` and `precision`", - "kind": "problem", - "name": "Use `int` arguments for `width` and `precision`", - "precision": "high", - "severity": "error", - "short_name": "UseIntArgumentForWidthAndPrecision", - "tags": [ - "correctness", - "security" - ] } ], "title": "Use valid format strings", From 23b72f7fc45da2982095799f216798186f3b971a Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 10 Aug 2022 19:59:57 +0200 Subject: [PATCH 06/31] FIO47-C fix literal number mismatch. --- .../src/rules/FIO47-C/UseValidSpecifiers.ql | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql index cafe5a6454..9a0a7b416c 100644 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql @@ -34,23 +34,25 @@ string getInvalidLength(string specChar) { specChar = ["p", "C", "S", "%"] and result = ["h", "hh", "l", "ll", "j", "z", "t", "L"] } -from FormatLiteral x, string message, string compatible, string specChar +from FormatLiteral x, string message where not isExcluded(x, IO4Package::useValidSpecifiersQuery()) and message = "The conversion specifier '" + x + "' is not valid." and - compatible = "" and - specChar = "" and not x.specsAreKnown() or - message = - "The conversion specifier '" + specChar + "' is not compatible with flags '" + compatible + "'" and - compatible = x.getFlags(_) and - specChar = x.getConversionChar(_) and - compatible.matches("%" + getInvalidFlag(specChar) + "%") - or - message = - "The conversion specifier '" + specChar + "' is not compatible with length '" + compatible + "'" and - compatible = x.getLength(_) and - specChar = x.getConversionChar(_) and - compatible.matches("%" + getInvalidLength(specChar) + "%") + exists(string compatible, string specChar, int n | + message = + "The conversion specifier '" + specChar + "' is not compatible with flags '" + compatible + + "'" and + compatible = x.getFlags(n) and + specChar = x.getConversionChar(n) and + compatible.matches("%" + getInvalidFlag(specChar) + "%") + or + message = + "The conversion specifier '" + specChar + "' is not compatible with length '" + compatible + + "'" and + compatible = x.getLength(n) and + specChar = x.getConversionChar(n) and + compatible.matches("%" + getInvalidLength(specChar) + "%") + ) select x, message From 716c7a1f0c0f16e0be5822b7aed1f5cf1acc1510 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 10 Aug 2022 22:25:17 +0200 Subject: [PATCH 07/31] FIO47-C fix check/cross symbols in markdown. --- c/cert/src/rules/FIO47-C/UseValidSpecifiers.md | 7 +++---- c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md | 7 +++---- c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md | 7 +++---- scripts/help/cert-help-extraction.py | 8 +++++++- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md index 13aa2f3471..3aa0ac89e7 100644 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md @@ -30,11 +30,10 @@ The following table summarizes the compliance of various conversion specificatio Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. -Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) +Valid and meaningful combinations are marked by the ✓ symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ❌ symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) -

Conversion
Specifier
Character

' XSI

-
+
SPACE


#


0


h


hh


l


ll


j


z


t


L

Argument
Type

d, i

(tick)

(tick)

(error)

(tick)

short

signed char

long

long long

intmax_t

size_t

ptrdiff_t

(error)

Signed integer

o

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

u

(tick)

(tick)

(error)

(tick)

unsigned short

unsigned  char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

x, X

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

f, F

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

e, E

(error)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

g, G

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

a, A

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

c

(error)

(tick)

(error)

(error)

(error)

(error)

wint_t

(error)

(error)

(error)

(error)

(error)

int or wint_t

s

(error)

(tick)

(error)

(error)

(error)

(error)

NTWS

(error)

(error)

(error)

(error)

(error)

NTBS or NTWS

p

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

void*

n

(error)

(error)

(error)

(error)

short*

char*

long*

long long*

intmax_t*

size_t*

ptrdiff_t*

(error)

Pointer to integer

C XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

wint_t

S XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

NTWS

%

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

None

- -SPACE: The space (" ") character
     N/E: No effect
     NTBS: char* argument pointing to a null-terminated character string
     NTWS: wchar_t* argument pointing to a null-terminated wide character string
     XSI: ISO/IEC 9945-2003 XSI extension +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md index d53e6337b6..47e8f9e038 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md @@ -30,11 +30,10 @@ The following table summarizes the compliance of various conversion specificatio Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. -Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) +Valid and meaningful combinations are marked by the ✓ symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ❌ symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) -

Conversion
Specifier
Character

' XSI

-
+
SPACE


#


0


h


hh


l


ll


j


z


t


L

Argument
Type

d, i

(tick)

(tick)

(error)

(tick)

short

signed char

long

long long

intmax_t

size_t

ptrdiff_t

(error)

Signed integer

o

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

u

(tick)

(tick)

(error)

(tick)

unsigned short

unsigned  char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

x, X

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

f, F

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

e, E

(error)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

g, G

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

a, A

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

c

(error)

(tick)

(error)

(error)

(error)

(error)

wint_t

(error)

(error)

(error)

(error)

(error)

int or wint_t

s

(error)

(tick)

(error)

(error)

(error)

(error)

NTWS

(error)

(error)

(error)

(error)

(error)

NTBS or NTWS

p

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

void*

n

(error)

(error)

(error)

(error)

short*

char*

long*

long long*

intmax_t*

size_t*

ptrdiff_t*

(error)

Pointer to integer

C XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

wint_t

S XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

NTWS

%

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

None

- -SPACE: The space (" ") character
     N/E: No effect
     NTBS: char* argument pointing to a null-terminated character string
     NTWS: wchar_t* argument pointing to a null-terminated wide character string
     XSI: ISO/IEC 9945-2003 XSI extension +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md index 4f5f5d74be..deaef01918 100644 --- a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.md @@ -30,11 +30,10 @@ The following table summarizes the compliance of various conversion specificatio Valid combinations are marked with a type name; arguments matched with the conversion specification are interpreted as that type. For example, an argument matched with the specifier `%hd` is interpreted as a `short`, so `short` appears in the cell where `d` and `h` intersect. The last column denotes the expected types of arguments matched with the original specifier characters. -Valid and meaningful combinations are marked by the ![](check.svg) symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ![](error.svg) symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) +Valid and meaningful combinations are marked by the ✓ symbol (save for the length modifier columns, as described previously). Valid combinations that have no effect are labeled *N/E*. Using a combination marked by the ❌ symbol, using a specification not represented in the table, or using an argument of an unexpected type is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See undefined behaviors [153](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_153), [155](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_155), [157](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_157), [158](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_158), [161](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_161), and [162](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_162).) -

Conversion
Specifier
Character

' XSI

-
+
SPACE


#


0


h


hh


l


ll


j


z


t


L

Argument
Type

d, i

(tick)

(tick)

(error)

(tick)

short

signed char

long

long long

intmax_t

size_t

ptrdiff_t

(error)

Signed integer

o

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

u

(tick)

(tick)

(error)

(tick)

unsigned short

unsigned  char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

x, X

(error)

(tick)

(tick)

(tick)

unsigned short

unsigned char

unsigned long

unsigned long long

uintmax_t

size_t

ptrdiff_t

(error)

Unsigned integer

f, F

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

e, E

(error)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

g, G

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

a, A

(tick)

(tick)

(tick)

(tick)

(error)

(error)

N/E

N/E

(error)

(error)

(error)

long double

double or long double

c

(error)

(tick)

(error)

(error)

(error)

(error)

wint_t

(error)

(error)

(error)

(error)

(error)

int or wint_t

s

(error)

(tick)

(error)

(error)

(error)

(error)

NTWS

(error)

(error)

(error)

(error)

(error)

NTBS or NTWS

p

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

void*

n

(error)

(error)

(error)

(error)

short*

char*

long*

long long*

intmax_t*

size_t*

ptrdiff_t*

(error)

Pointer to integer

C XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

wint_t

S XSI

(error)

(tick)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

NTWS

%

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

(error)

None

- -SPACE: The space (" ") character
     N/E: No effect
     NTBS: char* argument pointing to a null-terminated character string
     NTWS: wchar_t* argument pointing to a null-terminated wide character string
     XSI: ISO/IEC 9945-2003 XSI extension +
Conversion Specifier Character ' XSI - + SPACE \# 0 h hh l ll j z t L Argument Type
d , i short signed char long long long intmax_t size_t ptrdiff_t Signed integer
o unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
u unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
x , X unsigned short unsigned char unsigned long unsigned long long uintmax_t size_t ptrdiff_t Unsigned integer
f , F N/E N/E long double double or long double
e , E N/E N/E long double double or long double
g , G N/E N/E long double double or long double
a , A N/E N/E long double double or long double
c wint_t int or wint_t
s NTWS NTBS or NTWS
p void\*
n short\* char\* long\* long long\* intmax_t\* size_t\* ptrdiff_t\* Pointer to integer
C XSI wint_t
S XSI NTWS
% None
+SPACE: The space (`" "`) character* N/E*: No effect NTBS: `char*` argument pointing to a null-terminated character string NTWS: `wchar_t*` argument pointing to a null-terminated wide character string XSI: [ISO/IEC 9945-2003](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9945-2003) XSI extension The formatted input functions (`fscanf()` and related functions) use similarly specified format strings and impose similar restrictions on their format strings and arguments. diff --git a/scripts/help/cert-help-extraction.py b/scripts/help/cert-help-extraction.py index cdf0d09e07..cd185fa59d 100755 --- a/scripts/help/cert-help-extraction.py +++ b/scripts/help/cert-help-extraction.py @@ -282,7 +282,7 @@ def helper(node): if node.name == 'table' and 'data-macro-name' in node.attrs and node['data-macro-name'] == 'details' and 'data-macro-parameters' in node.attrs and node['data-macro-parameters'] == 'hidden=true': node.decompose() if node.name == 'img' and 'data-macro-name' in node.attrs and node['data-macro-name'] == 'anchor': - node.decompose() + node.decompose() # Retrieve Images if node.name == 'img' and 'src' in node.attrs and node['src'].startswith("/confluence") and not node['src'].startswith("/confluence/plugins/"): url = CERT_WIKI+node['src'] @@ -292,6 +292,12 @@ def helper(node): full_name = repo_root.joinpath(rule_path, filename) urllib.request.urlretrieve(url, full_name) node['src'] = filename + # Replace check.svg and error.svg images with unicode characters + if node.name == 'img': + if node['src'].endswith("check.svg"): + node.replace_with('\u2713') + elif node['src'].endswith("error.svg"): + node.replace_with('\u274C') # Unwrap , because can only contain text in QHelp if node.name == 'code' and node.find_parent('a'): node.unwrap() From 8f28a8822d8711682fab3506ccc54b16ecd498ac Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 11 Aug 2022 19:44:36 +0200 Subject: [PATCH 08/31] Rule ENV30-C and ENV31-C. --- ...tModifyTheReturnValueOfCertainFunctions.md | 225 ++++++++++++++++++ ...tModifyTheReturnValueOfCertainFunctions.ql | 100 ++++++++ ...vPointerIsInvalidAfterCertainOperations.md | 225 ++++++++++++++++++ ...vPointerIsInvalidAfterCertainOperations.ql | 46 ++++ ...yTheReturnValueOfCertainFunctions.expected | 69 ++++++ ...difyTheReturnValueOfCertainFunctions.qlref | 1 + c/cert/test/rules/ENV30-C/test.c | 83 +++++++ ...erIsInvalidAfterCertainOperations.expected | 6 + ...interIsInvalidAfterCertainOperations.qlref | 1 + c/cert/test/rules/ENV31-C/test.c | 48 ++++ .../cpp/exclusions/c/Contracts1.qll | 42 ++++ .../cpp/exclusions/c/RuleMetadata.qll | 3 + rule_packages/c/Contracts1.json | 48 ++++ rule_packages/c/IO4.json | 2 +- 14 files changed, 898 insertions(+), 1 deletion(-) create mode 100644 c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md create mode 100644 c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql create mode 100644 c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md create mode 100644 c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql create mode 100644 c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected create mode 100644 c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref create mode 100644 c/cert/test/rules/ENV30-C/test.c create mode 100644 c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected create mode 100644 c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref create mode 100644 c/cert/test/rules/ENV31-C/test.c create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll create mode 100644 rule_packages/c/Contracts1.json diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md new file mode 100644 index 0000000000..a68e904281 --- /dev/null +++ b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md @@ -0,0 +1,225 @@ +# ENV30-C: Do not modify the return value of certain functions + +This query implements the CERT-C rule ENV30-C: + +> Do not modify the object referenced by the return value of certain functions + + +## Description + +Some functions return a pointer to an object that cannot be modified without causing [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). These functions include `getenv()`, `setlocale()`, `localeconv()`, `asctime()`, and `strerror()`. In such cases, the function call results must be treated as being `const`-qualified. + +The C Standard, 7.22.4.6, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `getenv()` as follows: + +> The `getenv` function returns a pointer to a string associated with the matched list member. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `getenv` function. If the specified name cannot be found, a null pointer is returned. + + +If the string returned by `getenv()` must be altered, a local copy should be created. Altering the string returned by `getenv()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 184](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_184).) + +Similarly, subclause 7.11.1.1, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `setlocale()` as follows: + +> The pointer to string returned by the `setlocale` function is such that a subsequent call with that string value and its associated category will restore that part of the program'€™s locale. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `setlocale` function. + + +And subclause 7.11.2.1, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `localeconv()` as follows: + +> The `localeconv` function returns a pointer to the filled-in object. The structure pointed to by the return value shall not be modified by the program, but may be overwritten by a subsequent call to the `localeconv` function. In addition, calls to the `setlocale` function with categories `LC_ALL`, `LC_MONETARY`, or `LC_NUMERIC` may overwrite the contents of the structure. + + +Altering the string returned by `setlocale()` or the structure returned by `localeconv()` are [undefined behaviors](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behaviors 120](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_120) and [121](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_121).) Furthermore, the C Standard imposes no requirements on the contents of the string by `setlocale()`. Consequently, no assumptions can be made as to the string's internal contents or structure. + +Finally, subclause 7.24.6.2, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states + +> The `strerror` function returns a pointer to the string, the contents of which are locale-specific. The array pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `strerror` function. + + +Altering the string returned by `strerror()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 184](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_184).) + +## Noncompliant Code Example (getenv()) + +This noncompliant code example modifies the string returned by `getenv()` by replacing all double quotation marks (`"`) with underscores (`_`): + +```cpp +#include + +void trstr(char *c_str, char orig, char rep) { + while (*c_str != '\0') { + if (*c_str == orig) { + *c_str = rep; + } + ++c_str; + } +} + +void func(void) { + char *env = getenv("TEST_ENV"); + if (env == NULL) { + /* Handle error */ + } + trstr(env,'"', '_'); +} + +``` + +## Compliant Solution (getenv()) (Environment Not Modified) + +If the programmer does not intend to modify the environment, this compliant solution demonstrates how to modify a copy of the return value: + +```cpp +#include +#include + +void trstr(char *c_str, char orig, char rep) { + while (*c_str != '\0') { + if (*c_str == orig) { + *c_str = rep; + } + ++c_str; + } +} + +void func(void) { + const char *env; + char *copy_of_env; + + env = getenv("TEST_ENV"); + if (env == NULL) { + /* Handle error */ + } + + copy_of_env = (char *)malloc(strlen(env) + 1); + if (copy_of_env == NULL) { + /* Handle error */ + } + + strcpy(copy_of_env, env); + trstr(copy_of_env,'"', '_'); + /* ... */ + free(copy_of_env); +} +``` + +## Compliant Solution (getenv()) (Modifying the Environment in POSIX) + +If the programmer's intent is to modify the environment, this compliant solution, which saves the altered string back into the environment by using the POSIX `setenv()` and `strdup()` functions, can be used: + +```cpp +#include +#include + +void trstr(char *c_str, char orig, char rep) { + while (*c_str != '\0') { + if (*c_str == orig) { + *c_str = rep; + } + ++c_str; + } +} + +void func(void) { + const char *env; + char *copy_of_env; + + env = getenv("TEST_ENV"); + if (env == NULL) { + /* Handle error */ + } + + copy_of_env = strdup(env); + if (copy_of_env == NULL) { + /* Handle error */ + } + + trstr(copy_of_env,'"', '_'); + + if (setenv("TEST_ENV", copy_of_env, 1) != 0) { + /* Handle error */ + } + /* ... */ + free(copy_of_env); +} +``` + +## Noncompliant Code Example (localeconv()) + +In this noncompliant example, the object returned by `localeconv()` is directly modified: + +```cpp +#include + +void f2(void) { + struct lconv *conv = localeconv(); + + if ('\0' == conv->decimal_point[0]) { + conv->decimal_point = "."; + } +} + +``` + +## Compliant Solution (localeconv()) (Copy) + +This compliant solution modifies a copy of the object returned by `localeconv()`: + +```cpp +#include +#include +#include + +void f2(void) { + const struct lconv *conv = localeconv(); + if (conv == NULL) { + /* Handle error */ + } + + struct lconv *copy_of_conv = (struct lconv *)malloc( + sizeof(struct lconv)); + if (copy_of_conv == NULL) { + /* Handle error */ + } + + memcpy(copy_of_conv, conv, sizeof(struct lconv)); + + if ('\0' == copy_of_conv->decimal_point[0]) { + copy_of_conv->decimal_point = "."; + } + /* ... */ + free(copy_of_conv); +} +``` + +## Risk Assessment + +Modifying the object pointed to by the return value of `getenv()`, `setlocale()`, `localeconv()`, `asctime()`, or `strerror()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Even if the modification succeeds, the modified object can be overwritten by a subsequent call to the same function. + +
Rule Severity Likelihood Remediation Cost Priority Level
ENV30-C Low Probable Medium P4 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 stdlib-const-pointer-assign Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-ENV30
CodeSonar 7.0p0 BADFUNC.GETENV Use of getenv
Compass/ROSE Can detect violations of this rule. In particular, it ensures that the result of getenv() is stored in a const variable
Helix QAC 2022.2 C1492, C1493, C1494, C4751, C4752, C4753 C++4751, C++4752, C++4753
Klocwork 2022.2 MISRA.STDLIB.CTYPE.RANGE.2012_AMD1 MISRA.STDLIB.ILLEGAL_REUSE.2012_AMD1 MISRA.STDLIB.ILLEGAL_WRITE.2012_AMD1
LDRA tool suite 9.7.1 107 D Partially Implemented
Parasoft C/C++test 2022.1 CERT_C-ENV30-a The pointers returned by the Standard Library functions 'localeconv', 'getenv', 'setlocale' or, 'strerror' shall only be used as if they have pointer to const-qualified type
Polyspace Bug Finder R2022a CERT C: Rule ENV30-C Checks for modification of internal buffer returned from nonreentrant standard function (rule fully covered)
PRQA QA-C 9.7 1492, 1493, 1494
PVS-Studio 7.20 V675
RuleChecker 22.04 stdlib-const-pointer-assign Partially checked
+ + +## Related Vulnerabilities + +Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ENV30-C). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
ISO/IEC TS 17961:2013 Modifying the string returned by getenv , localeconv , setlocale , and strerror \[libmod\] Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ IEEE Std 1003.1:2013 \] XSH, System Interfaces, getenv XSH, System Interfaces, setlocale XSH, System Interfaces, localeconv
\[ ISO/IEC 9899:2011 \] 7.11.1.1, "The setlocale Function" 7.11.2.1, "The localeconv Function" 7.22.4.6, "The getenv Function" 7.24.6.2, "The strerror Function"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [ENV30-C: Do not modify the object referenced by the return value of certain functions](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql new file mode 100644 index 0000000000..a8e6216d99 --- /dev/null +++ b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql @@ -0,0 +1,100 @@ +/** + * @id c/cert/do-not-modify-the-return-value-of-certain-functions + * @name ENV30-C: Do not modify the return value of certain functions + * @description Return value of getenv and similar functions cannot be modified. + * @kind path-problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/env30-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import semmle.code.cpp.dataflow.DataFlow +import DataFlow::PathGraph + +/* + * Call to functions that return pointers to environment objects that should not be modified. + */ + +class NotModifiableCall extends FunctionCall { + NotModifiableCall() { + this.getTarget() + .hasGlobalOrStdName(["getenv", "setlocale", "localeconv", "asctime", "strerror"]) + } +} + +/* + * Returns an expression that modifies an object pointed by the return value of a call + * to one of the `NotModifiableCall` functions. + */ + +Expr objectModified(NotModifiableCall c) { + // the pointed object is reassigned + exists(AssignExpr ae | + result = + [ae.getLValue().(PointerDereferenceExpr), ae.getLValue().(PointerFieldAccess).getQualifier*()] and + //exclude overwrite from the same function + not ae.getRValue().(FunctionCall).getTarget().getName() = c.getTarget().getName() and + //exclude overwriting of localeconv returned object with setlocale() with categories LC_ALL (0) LC_MONETARY (3) LC_NUMERIC (4) + not ( + c.getTarget().getName() = "localeconv" and + ae.getRValue().(FunctionCall).getTarget().getName() = "setlocale" and + ae.getRValue().(FunctionCall).getArgument(0).getValue() = ["0", "3", "4"] + ) + ) +} + +/** + * DF configuration for flows from a `NotModifiableCall` to a object modifications. + */ +class DFConf extends DataFlow::Configuration { + DFConf() { this = "DFConf" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof NotModifiableCall + } + + override predicate isSink(DataFlow::Node sink) { any() } + + override predicate isBarrier(DataFlow::Node node) { + // exclude some functions that are known to be safe + exists(FunctionCall fc | + exists(string names | + names = ["strlen", "strdup", "string", "strcmp"] and + fc.getTarget().hasGlobalName(names) and + node.asExpr() = fc.getArgument(0) + or + names = ["memcpy", "memcpy_s", "strcpy", "strcpy_s", "strncpy", "strncpy_s"] and + node.asExpr() = fc.getArgument(1) and + ( + fc.getTarget().hasGlobalName(names) + or + exists(MacroInvocation mi | mi.getMacroName() = names and fc = mi.getExpr()) + ) + ) + ) + } + + // Flow through pointer dereference expressions + override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { + exists(PointerDereferenceExpr de | + de.getOperand() = node1.asExpr() and + de = node2.asExpr() + ) + } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink +where + not isExcluded(sink.getNode().asExpr(), + Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery()) and + sink != source and + sink.getNode().asExpr() = objectModified(source.getNode().asExpr()) and + // the modified object comes from a call to one of the ENV functions + any(DFConf d).hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "The object returned by the function " + + source.getNode().asExpr().(FunctionCall).getTarget().getName() + " should no be modified." diff --git a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md new file mode 100644 index 0000000000..bb222bfbce --- /dev/null +++ b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md @@ -0,0 +1,225 @@ +# ENV31-C: Do not rely on an env pointer following an operation that may invalidate it + +This query implements the CERT-C rule ENV31-C: + +> Do not rely on an environment pointer following an operation that may invalidate it + + +## Description + +Some implementations provide a nonportable environment pointer that is valid when `main()` is called but may be invalidated by operations that modify the environment. + +The C Standard, J.5.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], states + +> In a hosted environment, the main function receives a third argument, `char *envp[]`, that points to a null-terminated array of pointers to `char`, each of which points to a string that provides information about the environment for this execution of the program. + + +Consequently, under a [hosted environment](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions) supporting this common extension, it is possible to access the environment through a modified form of `main()`: + +```cpp +main(int argc, char *argv[], char *envp[]){ /* ... */ } + +``` +However, modifying the environment by any means may cause the environment memory to be reallocated, with the result that `envp` now references an incorrect location. For example, when compiled with GCC 4.8.1 and run on a 32-bit Intel GNU/Linux machine, the following code, + +```cpp +#include +#include + +extern char **environ; + +int main(int argc, const char *argv[], const char *envp[]) { + printf("environ: %p\n", environ); + printf("envp: %p\n", envp); + setenv("MY_NEW_VAR", "new_value", 1); + puts("--Added MY_NEW_VAR--"); + printf("environ: %p\n", environ); + printf("envp: %p\n", envp); + return 0; +} + +``` +yields + +```cpp +% ./envp-environ +environ: 0xbf8656ec +envp: 0xbf8656ec +--Added MY_NEW_VAR-- +environ: 0x804a008 +envp: 0xbf8656ec + +``` +It is evident from these results that the environment has been relocated as a result of the call to `setenv()`. The external variable `environ` is updated to refer to the current environment; the `envp` parameter is not. + +An environment pointer may also become invalidated by subsequent calls to `getenv().` (See [ENV34-C. Do not store pointers returned by certain functions](https://wiki.sei.cmu.edu/confluence/display/c/ENV34-C.+Do+not+store+pointers+returned+by+certain+functions) for more information.) + +## Noncompliant Code Example (POSIX) + +After a call to the POSIX `setenv()` function or to another function that modifies the environment, the `envp` pointer may no longer reference the current environment. The *Portable Operating System Interface (POSIX®), Base Specifications, Issue 7* \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\], states + +> Unanticipated results may occur if `setenv()` changes the external variable `environ`. In particular, if the optional `envp` argument to `main()` is present, it is not changed, and thus may point to an obsolete copy of the environment (as may any other copy of `environ`). + + +This noncompliant code example accesses the `envp` pointer after calling `setenv()`: + +```cpp +#include +#include + +int main(int argc, const char *argv[], const char *envp[]) { + if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { + /* Handle error */ + } + if (envp != NULL) { + for (size_t i = 0; envp[i] != NULL; ++i) { + puts(envp[i]); + } + } + return 0; +} + +``` +Because `envp` may no longer point to the current environment, this program has unanticipated behavior. + +## Compliant Solution (POSIX) + +Use `environ` in place of `envp` when defined: + +```cpp +#include +#include + +extern char **environ; + +int main(void) { + if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { + /* Handle error */ + } + if (environ != NULL) { + for (size_t i = 0; environ[i] != NULL; ++i) { + puts(environ[i]); + } + } + return 0; +} + +``` + +## Noncompliant Code Example (Windows) + +After a call to the Windows [_putenv_s()](http://msdn.microsoft.com/en-us/library/eyw7eyfw.aspx) function or to another function that modifies the environment, the `envp` pointer may no longer reference the environment. + +According to the Visual C++ reference \[[MSDN](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-MSDN)\] + +> The environment block passed to `main` and `wmain` is a "frozen" copy of the current environment. If you subsequently change the environment via a call to `_putenv` or `_wputenv`, the current environment (as returned by `getenv` / `_wgetenv` and the `_environ` / `_wenviron` variable) will change, but the block pointed to by `envp` will not change. + + +This noncompliant code example accesses the `envp` pointer after calling `_putenv_s()`: + +```cpp +#include +#include + +int main(int argc, const char *argv[], const char *envp[]) { + if (_putenv_s("MY_NEW_VAR", "new_value") != 0) { + /* Handle error */ + } + if (envp != NULL) { + for (size_t i = 0; envp[i] != NULL; ++i) { + puts(envp[i]); + } + } + return 0; +} + +``` +Because `envp` no longer points to the current environment, this program has unanticipated behavior. + +## Compliant Solution (Windows) + +This compliant solution uses the [_environ](http://msdn.microsoft.com/en-us/library/stxk41x1.aspx) variable in place of `envp`: + +```cpp +#include +#include + +_CRTIMP extern char **_environ; + +int main(int argc, const char *argv[]) { + if (_putenv_s("MY_NEW_VAR", "new_value") != 0) { + /* Handle error */ + } + if (_environ != NULL) { + for (size_t i = 0; _environ[i] != NULL; ++i) { + puts(_environ[i]); + } + } +return 0; +} + +``` + +## Compliant Solution + +This compliant solution can reduce remediation time when a large amount of noncompliant `envp` code exists. It replaces + +```cpp +int main(int argc, char *argv[], char *envp[]) { + /* ... */ +} + +``` +with + +```cpp +#if defined (_POSIX_) || defined (__USE_POSIX) + extern char **environ; + #define envp environ +#elif defined(_WIN32) + _CRTIMP extern char **_environ; + #define envp _environ +#endif + +int main(int argc, char *argv[]) { + /* ... */ +} + +``` +This compliant solution may need to be extended to support other implementations that support forms of the external variable `environ`. + +## Risk Assessment + +Using the `envp` environment pointer after the environment has been modified can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +
Rule Severity Likelihood Remediation Cost Priority Level
ENV31-C Low Probable Medium P4 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 Supported
Compass/ROSE
Helix QAC 2022.2 C4991, C4992, C4993 C++4991, C++4992, C++4993
LDRA tool suite 9.7.1 118 S Fully Implemented
Parasoft C/C++test 2022.1 CERT_C-ENV31-a Do not rely on an environment pointer following an operation that may invalidate it
Polyspace Bug Finder R2022a CERT C: Rule ENV31-C Checks for environment pointer invalidated by previous operation (rule fully covered)
PRQA QA-C 9.7 4991, 4992, 4993
PRQA QA-C++ 4.4 4991, 4992, 4993
+ + +## Related Vulnerabilities + +Search for vulnerabilities resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+MEM00-CPP). + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
CERT C VOID ENV31-CPP. Do not rely on an environment pointer following an operation that may invalidate it Prior to 2018-01-12: CERT: Unspecified Relationship
+ + +## Bibliography + +
\[ IEEE Std 1003.1:2013 \] XSH, System Interfaces, setenv
\[ ISO/IEC 9899:2011 \] J.5.1, "Environment Arguments"
\[ MSDN \] _environ , _wenviron , getenv , _wgetenv , _putenv_s , _wputenv_s
+ + +## Implementation notes + +None + +## References + +* CERT-C: [ENV31-C: Do not rely on an environment pointer following an operation that may invalidate it](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql new file mode 100644 index 0000000000..45e135737e --- /dev/null +++ b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql @@ -0,0 +1,46 @@ +/** + * @id c/cert/env-pointer-is-invalid-after-certain-operations + * @name ENV31-C: Do not rely on an env pointer following an operation that may invalidate it + * @description EnvironmentPointerIsInvalidAfterCertainOperations + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/env31-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +/* + * Call to functions that modify the environment. + */ + +class EnvModifyingCall extends FunctionCall { + EnvModifyingCall() { this.getTarget().hasGlobalOrStdName(["setenv", "_putenv_s"]) } +} + +/* + * Function main. + */ + +class MainFunction extends Function { + MainFunction() { + hasGlobalName("main") and + getType() instanceof IntType + } +} + +from VariableAccess va, MainFunction main, EnvModifyingCall call, Parameter envp +where + not isExcluded(va, Contracts1Package::envPointerIsInvalidAfterCertainOperationsQuery()) and + // param envp exists + main.getNumberOfParameters() >= 3 and + envp = main.getParameter(2) and + va.getTarget() = envp and + va = call.getASuccessor+() +select va, + "Accessing " + va + + " following a $@ is invalid because the optional $@ argument is present in main.", call, + call.toString(), envp, envp.getName() diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected new file mode 100644 index 0000000000..815c786f5e --- /dev/null +++ b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected @@ -0,0 +1,69 @@ +edges +| test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | +| test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | +| test.c:5:18:5:22 | c_str | test.c:6:10:6:15 | * ... | +| test.c:5:18:5:22 | c_str | test.c:6:11:6:15 | c_str | +| test.c:5:18:5:22 | c_str | test.c:7:9:7:14 | * ... | +| test.c:5:18:5:22 | c_str | test.c:7:10:7:14 | c_str | +| test.c:5:18:5:22 | c_str | test.c:8:7:8:12 | * ... | +| test.c:5:18:5:22 | c_str | test.c:8:8:8:12 | c_str | +| test.c:5:18:5:22 | c_str | test.c:10:7:10:11 | c_str | +| test.c:15:16:15:21 | call to getenv | test.c:17:3:17:20 | ... = ... | +| test.c:15:16:15:21 | call to getenv | test.c:17:17:17:20 | env1 | +| test.c:15:16:15:21 | call to getenv | test.c:19:7:19:10 | env1 | +| test.c:15:16:15:21 | call to getenv | test.c:21:9:21:12 | env1 | +| test.c:15:16:15:21 | call to getenv | test.c:21:9:21:12 | env1 | +| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | +| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | +| test.c:21:9:21:12 | env1 | test.c:21:9:21:12 | ref arg env1 | +| test.c:28:10:28:15 | call to getenv | test.c:28:3:28:27 | ... = ... | +| test.c:28:10:28:15 | call to getenv | test.c:29:7:29:10 | env2 | +| test.c:44:10:44:15 | call to getenv | test.c:44:3:44:27 | ... = ... | +| test.c:44:10:44:15 | call to getenv | test.c:45:7:45:10 | env3 | +| test.c:60:11:60:19 | call to setlocale | test.c:60:3:60:32 | ... = ... | +| test.c:60:11:60:19 | call to setlocale | test.c:62:15:62:19 | conv4 | +| test.c:60:11:60:19 | call to setlocale | test.c:63:5:63:9 | conv4 | +| test.c:68:31:68:40 | call to localeconv | test.c:69:7:69:11 | conv5 | +nodes +| test.c:5:18:5:22 | c_str | semmle.label | c_str | +| test.c:5:18:5:22 | c_str | semmle.label | c_str | +| test.c:5:18:5:22 | c_str | semmle.label | c_str | +| test.c:5:18:5:22 | c_str | semmle.label | c_str | +| test.c:6:10:6:15 | * ... | semmle.label | * ... | +| test.c:6:11:6:15 | c_str | semmle.label | c_str | +| test.c:7:9:7:14 | * ... | semmle.label | * ... | +| test.c:7:10:7:14 | c_str | semmle.label | c_str | +| test.c:8:7:8:12 | * ... | semmle.label | * ... | +| test.c:8:8:8:12 | c_str | semmle.label | c_str | +| test.c:10:7:10:11 | c_str | semmle.label | c_str | +| test.c:15:16:15:21 | call to getenv | semmle.label | call to getenv | +| test.c:15:16:15:21 | call to getenv | semmle.label | call to getenv | +| test.c:17:3:17:20 | ... = ... | semmle.label | ... = ... | +| test.c:17:17:17:20 | env1 | semmle.label | env1 | +| test.c:19:7:19:10 | env1 | semmle.label | env1 | +| test.c:21:9:21:12 | env1 | semmle.label | env1 | +| test.c:21:9:21:12 | env1 | semmle.label | env1 | +| test.c:21:9:21:12 | ref arg env1 | semmle.label | ref arg env1 | +| test.c:28:3:28:27 | ... = ... | semmle.label | ... = ... | +| test.c:28:10:28:15 | call to getenv | semmle.label | call to getenv | +| test.c:28:10:28:15 | call to getenv | semmle.label | call to getenv | +| test.c:29:7:29:10 | env2 | semmle.label | env2 | +| test.c:44:3:44:27 | ... = ... | semmle.label | ... = ... | +| test.c:44:10:44:15 | call to getenv | semmle.label | call to getenv | +| test.c:44:10:44:15 | call to getenv | semmle.label | call to getenv | +| test.c:45:7:45:10 | env3 | semmle.label | env3 | +| test.c:58:25:58:34 | call to localeconv | semmle.label | call to localeconv | +| test.c:60:3:60:32 | ... = ... | semmle.label | ... = ... | +| test.c:60:11:60:19 | call to setlocale | semmle.label | call to setlocale | +| test.c:60:11:60:19 | call to setlocale | semmle.label | call to setlocale | +| test.c:62:15:62:19 | conv4 | semmle.label | conv4 | +| test.c:63:5:63:9 | conv4 | semmle.label | conv4 | +| test.c:68:31:68:40 | call to localeconv | semmle.label | call to localeconv | +| test.c:68:31:68:40 | call to localeconv | semmle.label | call to localeconv | +| test.c:69:7:69:11 | conv5 | semmle.label | conv5 | +subpaths +| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | test.c:21:9:21:12 | ref arg env1 | +| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | test.c:21:9:21:12 | ref arg env1 | +#select +| test.c:8:7:8:12 | * ... | test.c:15:16:15:21 | call to getenv | test.c:8:7:8:12 | * ... | The object returned by the function getenv should no be modified. | +| test.c:63:5:63:9 | conv4 | test.c:60:11:60:19 | call to setlocale | test.c:63:5:63:9 | conv4 | The object returned by the function setlocale should no be modified. | diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref new file mode 100644 index 0000000000..9c9c06e9de --- /dev/null +++ b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref @@ -0,0 +1 @@ +rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql \ No newline at end of file diff --git a/c/cert/test/rules/ENV30-C/test.c b/c/cert/test/rules/ENV30-C/test.c new file mode 100644 index 0000000000..4ea7224abd --- /dev/null +++ b/c/cert/test/rules/ENV30-C/test.c @@ -0,0 +1,83 @@ +#include +#include +#include + +void trstr(char *c_str, char orig, char rep) { + while (*c_str != '\0') { + if (*c_str == orig) { + *c_str = rep; + } + ++c_str; + } +} + +void f1(void) { + char *env1 = getenv("TEST_ENV"); + char *copy_of_env; + copy_of_env = env1; // COMPLIANT + + if (env1 == NULL) { + } + trstr(env1, '"', '_'); // NON_COMPLIANT +} + +void f2(void) { + const char *env2; + char *copy_of_env; + + env2 = getenv("TEST_ENV"); + if (env2 == NULL) { + } + + copy_of_env = (char *)malloc(strlen(env2) + 1); + if (copy_of_env == NULL) { + } + + strcpy(copy_of_env, env2); + trstr(copy_of_env, '"', '_'); // COMPLIANT +} + +void f3(void) { + const char *env3; + char *copy_of_env; + + env3 = getenv("TEST_ENV"); + if (env3 == NULL) { + } + + copy_of_env = strdup(env3); + if (copy_of_env == NULL) { + } + + trstr(copy_of_env, '"', '_'); // COMPLIANT + if (setenv("TEST_ENV", copy_of_env, 1) != 0) { + } +} + +void f4(void) { + struct lconv *conv4 = localeconv(); + + conv4 = setlocale(LC_ALL, "C"); // COMPLIANT + + if ('\0' == conv4->decimal_point[0]) { + conv4->decimal_point = "."; // NON_COMPLIANT + } +} + +void f5(void) { + const struct lconv *conv5 = localeconv(); + if (conv5 == NULL) { + } + + struct lconv *copy_of_conv = (struct lconv *)malloc(sizeof(struct lconv)); + if (copy_of_conv == NULL) { + } + + memcpy(copy_of_conv, conv5, sizeof(struct lconv)); + + if ('\0' == copy_of_conv->decimal_point[0]) { + copy_of_conv->decimal_point = "."; // COMPLIANT + } + + free(copy_of_conv); +} \ No newline at end of file diff --git a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected new file mode 100644 index 0000000000..04d1511123 --- /dev/null +++ b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected @@ -0,0 +1,6 @@ +| test.c:26:9:26:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:18:9:18:14 | call to setenv | call to setenv | test.c:14:52:14:55 | envp | envp | +| test.c:27:26:27:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:18:9:18:14 | call to setenv | call to setenv | test.c:14:52:14:55 | envp | envp | +| test.c:28:14:28:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:18:9:18:14 | call to setenv | call to setenv | test.c:14:52:14:55 | envp | envp | +| test.c:41:9:41:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:33:9:33:17 | call to _putenv_s | call to _putenv_s | test.c:14:52:14:55 | envp | envp | +| test.c:42:26:42:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:33:9:33:17 | call to _putenv_s | call to _putenv_s | test.c:14:52:14:55 | envp | envp | +| test.c:43:14:43:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:33:9:33:17 | call to _putenv_s | call to _putenv_s | test.c:14:52:14:55 | envp | envp | diff --git a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref new file mode 100644 index 0000000000..1f9a564c8e --- /dev/null +++ b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref @@ -0,0 +1 @@ +rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql \ No newline at end of file diff --git a/c/cert/test/rules/ENV31-C/test.c b/c/cert/test/rules/ENV31-C/test.c new file mode 100644 index 0000000000..bd8845f28d --- /dev/null +++ b/c/cert/test/rules/ENV31-C/test.c @@ -0,0 +1,48 @@ +#include +#include + +// UNIX +extern char **environ; + +// WINDOWS +extern char **_environ; +int _putenv_s( + const char *varname, + const char *value_string +); + +int main(int argc, const char *argv[], const char *envp[]) { + + if (argc) { + // UNIX + if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { + /* Handle error */ + } + if (environ != NULL) { // COMPLIANT + for (size_t i = 0; environ[i] != NULL; ++i) { + puts(environ[i]); + } + } + if (envp != NULL) { // NON_COMPLIANT + for (size_t i = 0; envp[i] != NULL; ++i) { // NON_COMPLIANT + puts(envp[i]); // NON_COMPLIANT + } + } + } else { + // WINDOWS + if (_putenv_s("MY_NEW_VAR", "new_value") != 0) { + /* Handle error */ + } + if (_environ != NULL) { // COMPLIANT + for (size_t i = 0; _environ[i] != NULL; ++i) { + puts(_environ[i]); + } + } + if (envp != NULL) { // NON_COMPLIANT + for (size_t i = 0; envp[i] != NULL; ++i) { // NON_COMPLIANT + puts(envp[i]); // NON_COMPLIANT + } + } + } + return 0; +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll new file mode 100644 index 0000000000..b7a10673b7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll @@ -0,0 +1,42 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Contracts1Query = + TDoNotModifyTheReturnValueOfCertainFunctionsQuery() or + TEnvPointerIsInvalidAfterCertainOperationsQuery() + +predicate isContracts1QueryMetadata(Query query, string queryId, string ruleId) { + query = + // `Query` instance for the `doNotModifyTheReturnValueOfCertainFunctions` query + Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery() and + queryId = + // `@id` for the `doNotModifyTheReturnValueOfCertainFunctions` query + "c/cert/do-not-modify-the-return-value-of-certain-functions" and + ruleId = "ENV30-C" + or + query = + // `Query` instance for the `envPointerIsInvalidAfterCertainOperations` query + Contracts1Package::envPointerIsInvalidAfterCertainOperationsQuery() and + queryId = + // `@id` for the `envPointerIsInvalidAfterCertainOperations` query + "c/cert/env-pointer-is-invalid-after-certain-operations" and + ruleId = "ENV31-C" +} + +module Contracts1Package { + Query doNotModifyTheReturnValueOfCertainFunctionsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotModifyTheReturnValueOfCertainFunctions` query + TQueryC(TContracts1PackageQuery(TDoNotModifyTheReturnValueOfCertainFunctionsQuery())) + } + + Query envPointerIsInvalidAfterCertainOperationsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `envPointerIsInvalidAfterCertainOperations` query + TQueryC(TContracts1PackageQuery(TEnvPointerIsInvalidAfterCertainOperationsQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index e2c0f9e0e6..0c50ccffde 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -5,6 +5,7 @@ import codingstandards.cpp.exclusions.RuleMetadata import Banned import Concurrency1 import Concurrency2 +import Contracts1 import IO1 import IO2 import IO3 @@ -28,6 +29,7 @@ newtype TCQuery = TBannedPackageQuery(BannedQuery q) or TConcurrency1PackageQuery(Concurrency1Query q) or TConcurrency2PackageQuery(Concurrency2Query q) or + TContracts1PackageQuery(Contracts1Query q) or TIO1PackageQuery(IO1Query q) or TIO2PackageQuery(IO2Query q) or TIO3PackageQuery(IO3Query q) or @@ -51,6 +53,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) { isBannedQueryMetadata(query, queryId, ruleId) or isConcurrency1QueryMetadata(query, queryId, ruleId) or isConcurrency2QueryMetadata(query, queryId, ruleId) or + isContracts1QueryMetadata(query, queryId, ruleId) or isIO1QueryMetadata(query, queryId, ruleId) or isIO2QueryMetadata(query, queryId, ruleId) or isIO3QueryMetadata(query, queryId, ruleId) or diff --git a/rule_packages/c/Contracts1.json b/rule_packages/c/Contracts1.json new file mode 100644 index 0000000000..c3f477c9c4 --- /dev/null +++ b/rule_packages/c/Contracts1.json @@ -0,0 +1,48 @@ +{ + "CERT-C": { + "ENV30-C": { + "implementation_scope": { + "description": "None." + }, + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Return value of getenv and similar functions cannot be modified.", + "kind": "path-problem", + "name": "Do not modify the return value of certain functions", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotModifyTheReturnValueOfCertainFunctions", + "tags": [ + "correctness" + ] + } + ], + "title": "Do not modify the object referenced by the return value of certain functions" + }, + "ENV31-C": { + "implementation_scope": { + "description": "None." + }, + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "EnvironmentPointerIsInvalidAfterCertainOperations", + "kind": "problem", + "name": "Do not rely on an env pointer following an operation that may invalidate it", + "precision": "high", + "severity": "error", + "short_name": "EnvPointerIsInvalidAfterCertainOperations", + "tags": [ + "correctness" + ] + } + ], + "title": "Do not rely on an environment pointer following an operation that may invalidate it" + } + } +} \ No newline at end of file diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index bf92db99b8..5f099cec6b 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -20,7 +20,7 @@ ], "title": "Avoid TOCTOU race conditions while accessing files", "implementation_scope": { - "description": "The query is limited to one specific class of TOCTOU race conditions." + "description": "The query is limited to the specific class of TOCTOU race conditions." } }, "FIO47-C": { From 76f3ba7dbce9b3357e52ef832c7c470a242134c2 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 11 Aug 2022 19:45:18 +0200 Subject: [PATCH 09/31] Updated rules.csv --- rules.csv | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rules.csv b/rules.csv index a4b7d16efa..04a792033e 100755 --- a/rules.csv +++ b/rules.csv @@ -506,8 +506,8 @@ c,CERT-C,DCL38-C,Yes,Rule,,,Use the correct syntax when declaring a flexible arr c,CERT-C,DCL39-C,Yes,Rule,,,Avoid information leakage when passing a structure across a trust boundary,,Declarations,Hard, c,CERT-C,DCL40-C,Yes,Rule,,,Do not create incompatible declarations of the same function or object,,Declarations,Hard, c,CERT-C,DCL41-C,Yes,Rule,,,Do not declare variables inside a switch statement before the first case label,,Declarations,Medium, -c,CERT-C,ENV30-C,Yes,Rule,,,Do not modify the object referenced by the return value of certain functions,RULE-21-19,Contracts,Medium, -c,CERT-C,ENV31-C,Yes,Rule,,,Do not rely on an environment pointer following an operation that may invalidate it,RULE-21-20,Contracts,Hard, +c,CERT-C,ENV30-C,Yes,Rule,,,Do not modify the object referenced by the return value of certain functions,RULE-21-19,Contracts1,Medium, +c,CERT-C,ENV31-C,Yes,Rule,,,Do not rely on an environment pointer following an operation that may invalidate it,RULE-21-20,Contracts1,Hard, c,CERT-C,ENV32-C,Yes,Rule,,,All exit handlers must return normally,,Contracts,Medium, c,CERT-C,ENV33-C,Yes,Rule,,,Do not call system(),"RULE-21-21, M18-0-3",Banned,Easy, c,CERT-C,ENV34-C,Yes,Rule,,,Do not store pointers returned by certain functions,RULE-21-20,Contracts,Medium, @@ -761,7 +761,7 @@ c,MISRA-C-2012,RULE-21-15,Yes,Required,,,"The pointer arguments to the Standard c,MISRA-C-2012,RULE-21-16,Yes,Required,,,"The pointer arguments to the Standard Library function memcmp shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type",,Types,Medium, c,MISRA-C-2012,RULE-21-17,Yes,Mandatory,,,Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters,,Memory,Hard, c,MISRA-C-2012,RULE-21-18,Yes,Mandatory,,,The size_t argument passed to any function in shall have an appropriate value,,Expressions,Medium, -c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts1,Easy, +c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts,Easy, c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV31-C, ENV34-C",Contracts1,Easy, c,MISRA-C-2012,RULE-21-21,Yes,Required,,,The Standard Library function system of shall not be used,ENV33-C,Banned,Import, c,MISRA-C-2012,RULE-22-1,Yes,Required,,,All resources obtained dynamically by means of Standard Library functions shall be explicitly released,,Memory,Hard, From a931a689d238428ddfcd949580f5a1409cbe2f05 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 11 Aug 2022 19:48:59 +0200 Subject: [PATCH 10/31] Update rules.csv --- rules.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules.csv b/rules.csv index 04a792033e..0373acf7c9 100755 --- a/rules.csv +++ b/rules.csv @@ -762,7 +762,7 @@ c,MISRA-C-2012,RULE-21-16,Yes,Required,,,"The pointer arguments to the Standard c,MISRA-C-2012,RULE-21-17,Yes,Mandatory,,,Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters,,Memory,Hard, c,MISRA-C-2012,RULE-21-18,Yes,Mandatory,,,The size_t argument passed to any function in shall have an appropriate value,,Expressions,Medium, c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts,Easy, -c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV31-C, ENV34-C",Contracts1,Easy, +c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV31-C, ENV34-C",Contracts,Easy, c,MISRA-C-2012,RULE-21-21,Yes,Required,,,The Standard Library function system of shall not be used,ENV33-C,Banned,Import, c,MISRA-C-2012,RULE-22-1,Yes,Required,,,All resources obtained dynamically by means of Standard Library functions shall be explicitly released,,Memory,Hard, c,MISRA-C-2012,RULE-22-2,Yes,Mandatory,,,A block of memory shall only be freed if it was allocated by means of a Standard Library function,,Memory,Hard, From a2cb74f86a91d07cee030de9e627fded829981c4 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 11 Aug 2022 19:53:22 +0200 Subject: [PATCH 11/31] Format ENV31-C test case --- c/cert/test/rules/ENV31-C/test.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/c/cert/test/rules/ENV31-C/test.c b/c/cert/test/rules/ENV31-C/test.c index bd8845f28d..63fe9d7451 100644 --- a/c/cert/test/rules/ENV31-C/test.c +++ b/c/cert/test/rules/ENV31-C/test.c @@ -6,10 +6,7 @@ extern char **environ; // WINDOWS extern char **_environ; -int _putenv_s( - const char *varname, - const char *value_string -); +int _putenv_s(const char *varname, const char *value_string); int main(int argc, const char *argv[], const char *envp[]) { From dcf239e5a9194b8ba9c4c2f030f8f7449174e0a2 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 11 Aug 2022 20:34:10 +0200 Subject: [PATCH 12/31] Update expected file for ENV31-C --- ...nvPointerIsInvalidAfterCertainOperations.expected | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected index 04d1511123..7c42b5c817 100644 --- a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected +++ b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected @@ -1,6 +1,6 @@ -| test.c:26:9:26:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:18:9:18:14 | call to setenv | call to setenv | test.c:14:52:14:55 | envp | envp | -| test.c:27:26:27:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:18:9:18:14 | call to setenv | call to setenv | test.c:14:52:14:55 | envp | envp | -| test.c:28:14:28:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:18:9:18:14 | call to setenv | call to setenv | test.c:14:52:14:55 | envp | envp | -| test.c:41:9:41:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:33:9:33:17 | call to _putenv_s | call to _putenv_s | test.c:14:52:14:55 | envp | envp | -| test.c:42:26:42:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:33:9:33:17 | call to _putenv_s | call to _putenv_s | test.c:14:52:14:55 | envp | envp | -| test.c:43:14:43:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:33:9:33:17 | call to _putenv_s | call to _putenv_s | test.c:14:52:14:55 | envp | envp | +| test.c:23:9:23:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:15:9:15:14 | call to setenv | call to setenv | test.c:11:52:11:55 | envp | envp | +| test.c:24:26:24:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:15:9:15:14 | call to setenv | call to setenv | test.c:11:52:11:55 | envp | envp | +| test.c:25:14:25:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:15:9:15:14 | call to setenv | call to setenv | test.c:11:52:11:55 | envp | envp | +| test.c:38:9:38:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:30:9:30:17 | call to _putenv_s | call to _putenv_s | test.c:11:52:11:55 | envp | envp | +| test.c:39:26:39:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:30:9:30:17 | call to _putenv_s | call to _putenv_s | test.c:11:52:11:55 | envp | envp | +| test.c:40:14:40:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:30:9:30:17 | call to _putenv_s | call to _putenv_s | test.c:11:52:11:55 | envp | envp | From 188fd302e4b5b4cc35c90eaa9274f7c1ad7de77b Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 11 Aug 2022 22:36:24 +0200 Subject: [PATCH 13/31] Remove DF barriers --- ...tModifyTheReturnValueOfCertainFunctions.ql | 19 ------------------- ...yTheReturnValueOfCertainFunctions.expected | 8 ++++++++ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql index a8e6216d99..31a22294c5 100644 --- a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql +++ b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql @@ -59,25 +59,6 @@ class DFConf extends DataFlow::Configuration { override predicate isSink(DataFlow::Node sink) { any() } - override predicate isBarrier(DataFlow::Node node) { - // exclude some functions that are known to be safe - exists(FunctionCall fc | - exists(string names | - names = ["strlen", "strdup", "string", "strcmp"] and - fc.getTarget().hasGlobalName(names) and - node.asExpr() = fc.getArgument(0) - or - names = ["memcpy", "memcpy_s", "strcpy", "strcpy_s", "strncpy", "strncpy_s"] and - node.asExpr() = fc.getArgument(1) and - ( - fc.getTarget().hasGlobalName(names) - or - exists(MacroInvocation mi | mi.getMacroName() = names and fc = mi.getExpr()) - ) - ) - ) - } - // Flow through pointer dereference expressions override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { exists(PointerDereferenceExpr de | diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected index 815c786f5e..3ac6168cc6 100644 --- a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected +++ b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected @@ -18,12 +18,16 @@ edges | test.c:21:9:21:12 | env1 | test.c:21:9:21:12 | ref arg env1 | | test.c:28:10:28:15 | call to getenv | test.c:28:3:28:27 | ... = ... | | test.c:28:10:28:15 | call to getenv | test.c:29:7:29:10 | env2 | +| test.c:28:10:28:15 | call to getenv | test.c:32:39:32:42 | env2 | +| test.c:28:10:28:15 | call to getenv | test.c:36:23:36:26 | env2 | | test.c:44:10:44:15 | call to getenv | test.c:44:3:44:27 | ... = ... | | test.c:44:10:44:15 | call to getenv | test.c:45:7:45:10 | env3 | +| test.c:44:10:44:15 | call to getenv | test.c:48:24:48:27 | env3 | | test.c:60:11:60:19 | call to setlocale | test.c:60:3:60:32 | ... = ... | | test.c:60:11:60:19 | call to setlocale | test.c:62:15:62:19 | conv4 | | test.c:60:11:60:19 | call to setlocale | test.c:63:5:63:9 | conv4 | | test.c:68:31:68:40 | call to localeconv | test.c:69:7:69:11 | conv5 | +| test.c:68:31:68:40 | call to localeconv | test.c:76:24:76:28 | conv5 | nodes | test.c:5:18:5:22 | c_str | semmle.label | c_str | | test.c:5:18:5:22 | c_str | semmle.label | c_str | @@ -48,10 +52,13 @@ nodes | test.c:28:10:28:15 | call to getenv | semmle.label | call to getenv | | test.c:28:10:28:15 | call to getenv | semmle.label | call to getenv | | test.c:29:7:29:10 | env2 | semmle.label | env2 | +| test.c:32:39:32:42 | env2 | semmle.label | env2 | +| test.c:36:23:36:26 | env2 | semmle.label | env2 | | test.c:44:3:44:27 | ... = ... | semmle.label | ... = ... | | test.c:44:10:44:15 | call to getenv | semmle.label | call to getenv | | test.c:44:10:44:15 | call to getenv | semmle.label | call to getenv | | test.c:45:7:45:10 | env3 | semmle.label | env3 | +| test.c:48:24:48:27 | env3 | semmle.label | env3 | | test.c:58:25:58:34 | call to localeconv | semmle.label | call to localeconv | | test.c:60:3:60:32 | ... = ... | semmle.label | ... = ... | | test.c:60:11:60:19 | call to setlocale | semmle.label | call to setlocale | @@ -61,6 +68,7 @@ nodes | test.c:68:31:68:40 | call to localeconv | semmle.label | call to localeconv | | test.c:68:31:68:40 | call to localeconv | semmle.label | call to localeconv | | test.c:69:7:69:11 | conv5 | semmle.label | conv5 | +| test.c:76:24:76:28 | conv5 | semmle.label | conv5 | subpaths | test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | test.c:21:9:21:12 | ref arg env1 | | test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | test.c:21:9:21:12 | ref arg env1 | From 787fb4c04d1771290ac92d04f3967ee4bf4b7a6d Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Thu, 11 Aug 2022 23:40:05 +0200 Subject: [PATCH 14/31] consider increment operator --- .../ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql | 2 ++ .../DoNotModifyTheReturnValueOfCertainFunctions.expected | 1 + c/cert/test/rules/ENV30-C/test.c | 6 +++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql index 31a22294c5..522959dd53 100644 --- a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql +++ b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql @@ -45,6 +45,8 @@ Expr objectModified(NotModifiableCall c) { ae.getRValue().(FunctionCall).getArgument(0).getValue() = ["0", "3", "4"] ) ) + or + exists(CrementOperation co | result = co.getOperand()) } /** diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected index 3ac6168cc6..effc07af72 100644 --- a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected +++ b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected @@ -74,4 +74,5 @@ subpaths | test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | test.c:21:9:21:12 | ref arg env1 | #select | test.c:8:7:8:12 | * ... | test.c:15:16:15:21 | call to getenv | test.c:8:7:8:12 | * ... | The object returned by the function getenv should no be modified. | +| test.c:10:7:10:11 | c_str | test.c:15:16:15:21 | call to getenv | test.c:10:7:10:11 | c_str | The object returned by the function getenv should no be modified. | | test.c:63:5:63:9 | conv4 | test.c:60:11:60:19 | call to setlocale | test.c:63:5:63:9 | conv4 | The object returned by the function setlocale should no be modified. | diff --git a/c/cert/test/rules/ENV30-C/test.c b/c/cert/test/rules/ENV30-C/test.c index 4ea7224abd..5240eee6e3 100644 --- a/c/cert/test/rules/ENV30-C/test.c +++ b/c/cert/test/rules/ENV30-C/test.c @@ -5,9 +5,9 @@ void trstr(char *c_str, char orig, char rep) { while (*c_str != '\0') { if (*c_str == orig) { - *c_str = rep; + *c_str = rep; // NON_COMPLIANT } - ++c_str; + ++c_str; // NON_COMPLIANT } } @@ -18,7 +18,7 @@ void f1(void) { if (env1 == NULL) { } - trstr(env1, '"', '_'); // NON_COMPLIANT + trstr(env1, '"', '_'); } void f2(void) { From cb469e60b61d8cf6f57090cee84deb6cf2a6a5f1 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Fri, 12 Aug 2022 00:52:05 +0200 Subject: [PATCH 15/31] Simplified ENV30-C --- ...tModifyTheReturnValueOfCertainFunctions.ql | 41 ++++------- ...yTheReturnValueOfCertainFunctions.expected | 72 ++----------------- c/cert/test/rules/ENV30-C/test.c | 5 +- 3 files changed, 21 insertions(+), 97 deletions(-) diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql index 522959dd53..78e7055dcf 100644 --- a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql +++ b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql @@ -27,26 +27,21 @@ class NotModifiableCall extends FunctionCall { } /* - * Returns an expression that modifies an object pointed by the return value of a call - * to one of the `NotModifiableCall` functions. + * An expression that modifies an object. */ -Expr objectModified(NotModifiableCall c) { - // the pointed object is reassigned - exists(AssignExpr ae | - result = - [ae.getLValue().(PointerDereferenceExpr), ae.getLValue().(PointerFieldAccess).getQualifier*()] and - //exclude overwrite from the same function - not ae.getRValue().(FunctionCall).getTarget().getName() = c.getTarget().getName() and - //exclude overwriting of localeconv returned object with setlocale() with categories LC_ALL (0) LC_MONETARY (3) LC_NUMERIC (4) - not ( - c.getTarget().getName() = "localeconv" and - ae.getRValue().(FunctionCall).getTarget().getName() = "setlocale" and - ae.getRValue().(FunctionCall).getArgument(0).getValue() = ["0", "3", "4"] +class ObjectWrite extends Expr { + ObjectWrite() { + // the pointed object is reassigned + exists(Expr e | + e = [any(AssignExpr ae).getLValue(), any(CrementOperation co).getOperand()] and + ( + this = e.(PointerDereferenceExpr).getOperand() + or + this = e.(PointerFieldAccess).getQualifier() + ) ) - ) - or - exists(CrementOperation co | result = co.getOperand()) + } } /** @@ -59,23 +54,13 @@ class DFConf extends DataFlow::Configuration { source.asExpr() instanceof NotModifiableCall } - override predicate isSink(DataFlow::Node sink) { any() } - - // Flow through pointer dereference expressions - override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - exists(PointerDereferenceExpr de | - de.getOperand() = node1.asExpr() and - de = node2.asExpr() - ) - } + override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ObjectWrite } } from DataFlow::PathNode source, DataFlow::PathNode sink where not isExcluded(sink.getNode().asExpr(), Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery()) and - sink != source and - sink.getNode().asExpr() = objectModified(source.getNode().asExpr()) and // the modified object comes from a call to one of the ENV functions any(DFConf d).hasFlowPath(source, sink) select sink.getNode(), source, sink, diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected index effc07af72..bac3842044 100644 --- a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected +++ b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected @@ -1,78 +1,16 @@ edges -| test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | -| test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | -| test.c:5:18:5:22 | c_str | test.c:6:10:6:15 | * ... | -| test.c:5:18:5:22 | c_str | test.c:6:11:6:15 | c_str | -| test.c:5:18:5:22 | c_str | test.c:7:9:7:14 | * ... | -| test.c:5:18:5:22 | c_str | test.c:7:10:7:14 | c_str | -| test.c:5:18:5:22 | c_str | test.c:8:7:8:12 | * ... | | test.c:5:18:5:22 | c_str | test.c:8:8:8:12 | c_str | -| test.c:5:18:5:22 | c_str | test.c:10:7:10:11 | c_str | -| test.c:15:16:15:21 | call to getenv | test.c:17:3:17:20 | ... = ... | -| test.c:15:16:15:21 | call to getenv | test.c:17:17:17:20 | env1 | -| test.c:15:16:15:21 | call to getenv | test.c:19:7:19:10 | env1 | | test.c:15:16:15:21 | call to getenv | test.c:21:9:21:12 | env1 | -| test.c:15:16:15:21 | call to getenv | test.c:21:9:21:12 | env1 | -| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | | test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | -| test.c:21:9:21:12 | env1 | test.c:21:9:21:12 | ref arg env1 | -| test.c:28:10:28:15 | call to getenv | test.c:28:3:28:27 | ... = ... | -| test.c:28:10:28:15 | call to getenv | test.c:29:7:29:10 | env2 | -| test.c:28:10:28:15 | call to getenv | test.c:32:39:32:42 | env2 | -| test.c:28:10:28:15 | call to getenv | test.c:36:23:36:26 | env2 | -| test.c:44:10:44:15 | call to getenv | test.c:44:3:44:27 | ... = ... | -| test.c:44:10:44:15 | call to getenv | test.c:45:7:45:10 | env3 | -| test.c:44:10:44:15 | call to getenv | test.c:48:24:48:27 | env3 | -| test.c:60:11:60:19 | call to setlocale | test.c:60:3:60:32 | ... = ... | -| test.c:60:11:60:19 | call to setlocale | test.c:62:15:62:19 | conv4 | -| test.c:60:11:60:19 | call to setlocale | test.c:63:5:63:9 | conv4 | -| test.c:68:31:68:40 | call to localeconv | test.c:69:7:69:11 | conv5 | -| test.c:68:31:68:40 | call to localeconv | test.c:76:24:76:28 | conv5 | +| test.c:61:11:61:20 | call to localeconv | test.c:64:5:64:9 | conv4 | nodes | test.c:5:18:5:22 | c_str | semmle.label | c_str | -| test.c:5:18:5:22 | c_str | semmle.label | c_str | -| test.c:5:18:5:22 | c_str | semmle.label | c_str | -| test.c:5:18:5:22 | c_str | semmle.label | c_str | -| test.c:6:10:6:15 | * ... | semmle.label | * ... | -| test.c:6:11:6:15 | c_str | semmle.label | c_str | -| test.c:7:9:7:14 | * ... | semmle.label | * ... | -| test.c:7:10:7:14 | c_str | semmle.label | c_str | -| test.c:8:7:8:12 | * ... | semmle.label | * ... | | test.c:8:8:8:12 | c_str | semmle.label | c_str | -| test.c:10:7:10:11 | c_str | semmle.label | c_str | -| test.c:15:16:15:21 | call to getenv | semmle.label | call to getenv | | test.c:15:16:15:21 | call to getenv | semmle.label | call to getenv | -| test.c:17:3:17:20 | ... = ... | semmle.label | ... = ... | -| test.c:17:17:17:20 | env1 | semmle.label | env1 | -| test.c:19:7:19:10 | env1 | semmle.label | env1 | -| test.c:21:9:21:12 | env1 | semmle.label | env1 | | test.c:21:9:21:12 | env1 | semmle.label | env1 | -| test.c:21:9:21:12 | ref arg env1 | semmle.label | ref arg env1 | -| test.c:28:3:28:27 | ... = ... | semmle.label | ... = ... | -| test.c:28:10:28:15 | call to getenv | semmle.label | call to getenv | -| test.c:28:10:28:15 | call to getenv | semmle.label | call to getenv | -| test.c:29:7:29:10 | env2 | semmle.label | env2 | -| test.c:32:39:32:42 | env2 | semmle.label | env2 | -| test.c:36:23:36:26 | env2 | semmle.label | env2 | -| test.c:44:3:44:27 | ... = ... | semmle.label | ... = ... | -| test.c:44:10:44:15 | call to getenv | semmle.label | call to getenv | -| test.c:44:10:44:15 | call to getenv | semmle.label | call to getenv | -| test.c:45:7:45:10 | env3 | semmle.label | env3 | -| test.c:48:24:48:27 | env3 | semmle.label | env3 | -| test.c:58:25:58:34 | call to localeconv | semmle.label | call to localeconv | -| test.c:60:3:60:32 | ... = ... | semmle.label | ... = ... | -| test.c:60:11:60:19 | call to setlocale | semmle.label | call to setlocale | -| test.c:60:11:60:19 | call to setlocale | semmle.label | call to setlocale | -| test.c:62:15:62:19 | conv4 | semmle.label | conv4 | -| test.c:63:5:63:9 | conv4 | semmle.label | conv4 | -| test.c:68:31:68:40 | call to localeconv | semmle.label | call to localeconv | -| test.c:68:31:68:40 | call to localeconv | semmle.label | call to localeconv | -| test.c:69:7:69:11 | conv5 | semmle.label | conv5 | -| test.c:76:24:76:28 | conv5 | semmle.label | conv5 | +| test.c:61:11:61:20 | call to localeconv | semmle.label | call to localeconv | +| test.c:64:5:64:9 | conv4 | semmle.label | conv4 | subpaths -| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | test.c:21:9:21:12 | ref arg env1 | -| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | test.c:5:18:5:22 | c_str | test.c:21:9:21:12 | ref arg env1 | #select -| test.c:8:7:8:12 | * ... | test.c:15:16:15:21 | call to getenv | test.c:8:7:8:12 | * ... | The object returned by the function getenv should no be modified. | -| test.c:10:7:10:11 | c_str | test.c:15:16:15:21 | call to getenv | test.c:10:7:10:11 | c_str | The object returned by the function getenv should no be modified. | -| test.c:63:5:63:9 | conv4 | test.c:60:11:60:19 | call to setlocale | test.c:63:5:63:9 | conv4 | The object returned by the function setlocale should no be modified. | +| test.c:8:8:8:12 | c_str | test.c:15:16:15:21 | call to getenv | test.c:8:8:8:12 | c_str | The object returned by the function getenv should no be modified. | +| test.c:64:5:64:9 | conv4 | test.c:61:11:61:20 | call to localeconv | test.c:64:5:64:9 | conv4 | The object returned by the function localeconv should no be modified. | diff --git a/c/cert/test/rules/ENV30-C/test.c b/c/cert/test/rules/ENV30-C/test.c index 5240eee6e3..534298cf40 100644 --- a/c/cert/test/rules/ENV30-C/test.c +++ b/c/cert/test/rules/ENV30-C/test.c @@ -7,7 +7,7 @@ void trstr(char *c_str, char orig, char rep) { if (*c_str == orig) { *c_str = rep; // NON_COMPLIANT } - ++c_str; // NON_COMPLIANT + ++c_str; } } @@ -57,7 +57,8 @@ void f3(void) { void f4(void) { struct lconv *conv4 = localeconv(); - conv4 = setlocale(LC_ALL, "C"); // COMPLIANT + setlocale(LC_ALL, "C"); // COMPLIANT + conv4 = localeconv(); // COMPLIANT if ('\0' == conv4->decimal_point[0]) { conv4->decimal_point = "."; // NON_COMPLIANT From 7748bdfe5cdf416f0456afaf612edb0f98973d09 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Fri, 12 Aug 2022 01:00:32 +0200 Subject: [PATCH 16/31] Add a test for ENV30-C --- .../DoNotModifyTheReturnValueOfCertainFunctions.expected | 4 ++++ c/cert/test/rules/ENV30-C/test.c | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected index bac3842044..91d6d9a1b4 100644 --- a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected +++ b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected @@ -3,6 +3,7 @@ edges | test.c:15:16:15:21 | call to getenv | test.c:21:9:21:12 | env1 | | test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | | test.c:61:11:61:20 | call to localeconv | test.c:64:5:64:9 | conv4 | +| test.c:69:25:69:34 | call to localeconv | test.c:73:5:73:8 | conv | nodes | test.c:5:18:5:22 | c_str | semmle.label | c_str | | test.c:8:8:8:12 | c_str | semmle.label | c_str | @@ -10,7 +11,10 @@ nodes | test.c:21:9:21:12 | env1 | semmle.label | env1 | | test.c:61:11:61:20 | call to localeconv | semmle.label | call to localeconv | | test.c:64:5:64:9 | conv4 | semmle.label | conv4 | +| test.c:69:25:69:34 | call to localeconv | semmle.label | call to localeconv | +| test.c:73:5:73:8 | conv | semmle.label | conv | subpaths #select | test.c:8:8:8:12 | c_str | test.c:15:16:15:21 | call to getenv | test.c:8:8:8:12 | c_str | The object returned by the function getenv should no be modified. | | test.c:64:5:64:9 | conv4 | test.c:61:11:61:20 | call to localeconv | test.c:64:5:64:9 | conv4 | The object returned by the function localeconv should no be modified. | +| test.c:73:5:73:8 | conv | test.c:69:25:69:34 | call to localeconv | test.c:73:5:73:8 | conv | The object returned by the function localeconv should no be modified. | diff --git a/c/cert/test/rules/ENV30-C/test.c b/c/cert/test/rules/ENV30-C/test.c index 534298cf40..cd7c101898 100644 --- a/c/cert/test/rules/ENV30-C/test.c +++ b/c/cert/test/rules/ENV30-C/test.c @@ -65,6 +65,15 @@ void f4(void) { } } +void f4alias(void) { + struct lconv *conv4 = localeconv(); + struct lconv *conv = conv4; + + if ('\0' == conv4->decimal_point[0]) { + conv->decimal_point = "."; // NON_COMPLIANT + } +} + void f5(void) { const struct lconv *conv5 = localeconv(); if (conv5 == NULL) { From b285364c2e0c22c747fbf0ea189caab2031c8a98 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Fri, 12 Aug 2022 20:30:05 +0200 Subject: [PATCH 17/31] IO4 --- ...tModifyTheReturnValueOfCertainFunctions.md | 225 ------------------ ...tModifyTheReturnValueOfCertainFunctions.ql | 68 ------ ...vPointerIsInvalidAfterCertainOperations.md | 225 ------------------ ...vPointerIsInvalidAfterCertainOperations.ql | 46 ---- ...ToctouRaceConditionsWhileAccessingFiles.ql | 2 +- .../src/rules/FIO47-C/UseValidSpecifiers.ql | 2 +- .../FIO47-C/WrongNumberOfFormatArguments.ql | 2 +- .../rules/FIO47-C/WrongTypeFormatArguments.ql | 2 +- c/cert/src/rules/FIO47-C/check.svg | 1 - c/cert/src/rules/FIO47-C/error.svg | 1 - ...yTheReturnValueOfCertainFunctions.expected | 20 -- ...difyTheReturnValueOfCertainFunctions.qlref | 1 - c/cert/test/rules/ENV30-C/test.c | 93 -------- ...erIsInvalidAfterCertainOperations.expected | 6 - ...interIsInvalidAfterCertainOperations.qlref | 1 - c/cert/test/rules/ENV31-C/test.c | 45 ---- .../cpp/exclusions/c/Contracts1.qll | 42 ---- .../cpp/exclusions/c/RuleMetadata.qll | 3 - rule_packages/c/Contracts1.json | 48 ---- rule_packages/c/IO1.json | 6 +- rule_packages/c/IO3.json | 2 +- rule_packages/c/IO4.json | 10 +- rules.csv | 4 +- 23 files changed, 15 insertions(+), 840 deletions(-) delete mode 100644 c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md delete mode 100644 c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql delete mode 100644 c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md delete mode 100644 c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql delete mode 100644 c/cert/src/rules/FIO47-C/check.svg delete mode 100644 c/cert/src/rules/FIO47-C/error.svg delete mode 100644 c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected delete mode 100644 c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref delete mode 100644 c/cert/test/rules/ENV30-C/test.c delete mode 100644 c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected delete mode 100644 c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref delete mode 100644 c/cert/test/rules/ENV31-C/test.c delete mode 100644 cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll delete mode 100644 rule_packages/c/Contracts1.json diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md deleted file mode 100644 index a68e904281..0000000000 --- a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.md +++ /dev/null @@ -1,225 +0,0 @@ -# ENV30-C: Do not modify the return value of certain functions - -This query implements the CERT-C rule ENV30-C: - -> Do not modify the object referenced by the return value of certain functions - - -## Description - -Some functions return a pointer to an object that cannot be modified without causing [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). These functions include `getenv()`, `setlocale()`, `localeconv()`, `asctime()`, and `strerror()`. In such cases, the function call results must be treated as being `const`-qualified. - -The C Standard, 7.22.4.6, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `getenv()` as follows: - -> The `getenv` function returns a pointer to a string associated with the matched list member. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `getenv` function. If the specified name cannot be found, a null pointer is returned. - - -If the string returned by `getenv()` must be altered, a local copy should be created. Altering the string returned by `getenv()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 184](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_184).) - -Similarly, subclause 7.11.1.1, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `setlocale()` as follows: - -> The pointer to string returned by the `setlocale` function is such that a subsequent call with that string value and its associated category will restore that part of the program'€™s locale. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `setlocale` function. - - -And subclause 7.11.2.1, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `localeconv()` as follows: - -> The `localeconv` function returns a pointer to the filled-in object. The structure pointed to by the return value shall not be modified by the program, but may be overwritten by a subsequent call to the `localeconv` function. In addition, calls to the `setlocale` function with categories `LC_ALL`, `LC_MONETARY`, or `LC_NUMERIC` may overwrite the contents of the structure. - - -Altering the string returned by `setlocale()` or the structure returned by `localeconv()` are [undefined behaviors](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behaviors 120](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_120) and [121](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_121).) Furthermore, the C Standard imposes no requirements on the contents of the string by `setlocale()`. Consequently, no assumptions can be made as to the string's internal contents or structure. - -Finally, subclause 7.24.6.2, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states - -> The `strerror` function returns a pointer to the string, the contents of which are locale-specific. The array pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `strerror` function. - - -Altering the string returned by `strerror()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 184](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_184).) - -## Noncompliant Code Example (getenv()) - -This noncompliant code example modifies the string returned by `getenv()` by replacing all double quotation marks (`"`) with underscores (`_`): - -```cpp -#include - -void trstr(char *c_str, char orig, char rep) { - while (*c_str != '\0') { - if (*c_str == orig) { - *c_str = rep; - } - ++c_str; - } -} - -void func(void) { - char *env = getenv("TEST_ENV"); - if (env == NULL) { - /* Handle error */ - } - trstr(env,'"', '_'); -} - -``` - -## Compliant Solution (getenv()) (Environment Not Modified) - -If the programmer does not intend to modify the environment, this compliant solution demonstrates how to modify a copy of the return value: - -```cpp -#include -#include - -void trstr(char *c_str, char orig, char rep) { - while (*c_str != '\0') { - if (*c_str == orig) { - *c_str = rep; - } - ++c_str; - } -} - -void func(void) { - const char *env; - char *copy_of_env; - - env = getenv("TEST_ENV"); - if (env == NULL) { - /* Handle error */ - } - - copy_of_env = (char *)malloc(strlen(env) + 1); - if (copy_of_env == NULL) { - /* Handle error */ - } - - strcpy(copy_of_env, env); - trstr(copy_of_env,'"', '_'); - /* ... */ - free(copy_of_env); -} -``` - -## Compliant Solution (getenv()) (Modifying the Environment in POSIX) - -If the programmer's intent is to modify the environment, this compliant solution, which saves the altered string back into the environment by using the POSIX `setenv()` and `strdup()` functions, can be used: - -```cpp -#include -#include - -void trstr(char *c_str, char orig, char rep) { - while (*c_str != '\0') { - if (*c_str == orig) { - *c_str = rep; - } - ++c_str; - } -} - -void func(void) { - const char *env; - char *copy_of_env; - - env = getenv("TEST_ENV"); - if (env == NULL) { - /* Handle error */ - } - - copy_of_env = strdup(env); - if (copy_of_env == NULL) { - /* Handle error */ - } - - trstr(copy_of_env,'"', '_'); - - if (setenv("TEST_ENV", copy_of_env, 1) != 0) { - /* Handle error */ - } - /* ... */ - free(copy_of_env); -} -``` - -## Noncompliant Code Example (localeconv()) - -In this noncompliant example, the object returned by `localeconv()` is directly modified: - -```cpp -#include - -void f2(void) { - struct lconv *conv = localeconv(); - - if ('\0' == conv->decimal_point[0]) { - conv->decimal_point = "."; - } -} - -``` - -## Compliant Solution (localeconv()) (Copy) - -This compliant solution modifies a copy of the object returned by `localeconv()`: - -```cpp -#include -#include -#include - -void f2(void) { - const struct lconv *conv = localeconv(); - if (conv == NULL) { - /* Handle error */ - } - - struct lconv *copy_of_conv = (struct lconv *)malloc( - sizeof(struct lconv)); - if (copy_of_conv == NULL) { - /* Handle error */ - } - - memcpy(copy_of_conv, conv, sizeof(struct lconv)); - - if ('\0' == copy_of_conv->decimal_point[0]) { - copy_of_conv->decimal_point = "."; - } - /* ... */ - free(copy_of_conv); -} -``` - -## Risk Assessment - -Modifying the object pointed to by the return value of `getenv()`, `setlocale()`, `localeconv()`, `asctime()`, or `strerror()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Even if the modification succeeds, the modified object can be overwritten by a subsequent call to the same function. - -
Rule Severity Likelihood Remediation Cost Priority Level
ENV30-C Low Probable Medium P4 L3
- - -## Automated Detection - -
Tool Version Checker Description
Astrée 22.04 stdlib-const-pointer-assign Partially checked
Axivion Bauhaus Suite 7.2.0 CertC-ENV30
CodeSonar 7.0p0 BADFUNC.GETENV Use of getenv
Compass/ROSE Can detect violations of this rule. In particular, it ensures that the result of getenv() is stored in a const variable
Helix QAC 2022.2 C1492, C1493, C1494, C4751, C4752, C4753 C++4751, C++4752, C++4753
Klocwork 2022.2 MISRA.STDLIB.CTYPE.RANGE.2012_AMD1 MISRA.STDLIB.ILLEGAL_REUSE.2012_AMD1 MISRA.STDLIB.ILLEGAL_WRITE.2012_AMD1
LDRA tool suite 9.7.1 107 D Partially Implemented
Parasoft C/C++test 2022.1 CERT_C-ENV30-a The pointers returned by the Standard Library functions 'localeconv', 'getenv', 'setlocale' or, 'strerror' shall only be used as if they have pointer to const-qualified type
Polyspace Bug Finder R2022a CERT C: Rule ENV30-C Checks for modification of internal buffer returned from nonreentrant standard function (rule fully covered)
PRQA QA-C 9.7 1492, 1493, 1494
PVS-Studio 7.20 V675
RuleChecker 22.04 stdlib-const-pointer-assign Partially checked
- - -## Related Vulnerabilities - -Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+ENV30-C). - -## Related Guidelines - -[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) - -
Taxonomy Taxonomy item Relationship
ISO/IEC TS 17961:2013 Modifying the string returned by getenv , localeconv , setlocale , and strerror \[libmod\] Prior to 2018-01-12: CERT: Unspecified Relationship
- - -## Bibliography - -
\[ IEEE Std 1003.1:2013 \] XSH, System Interfaces, getenv XSH, System Interfaces, setlocale XSH, System Interfaces, localeconv
\[ ISO/IEC 9899:2011 \] 7.11.1.1, "The setlocale Function" 7.11.2.1, "The localeconv Function" 7.22.4.6, "The getenv Function" 7.24.6.2, "The strerror Function"
- - -## Implementation notes - -None - -## References - -* CERT-C: [ENV30-C: Do not modify the object referenced by the return value of certain functions](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql b/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql deleted file mode 100644 index 78e7055dcf..0000000000 --- a/c/cert/src/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @id c/cert/do-not-modify-the-return-value-of-certain-functions - * @name ENV30-C: Do not modify the return value of certain functions - * @description Return value of getenv and similar functions cannot be modified. - * @kind path-problem - * @precision very-high - * @problem.severity error - * @tags external/cert/id/env30-c - * correctness - * external/cert/obligation/rule - */ - -import cpp -import codingstandards.c.cert -import semmle.code.cpp.dataflow.DataFlow -import DataFlow::PathGraph - -/* - * Call to functions that return pointers to environment objects that should not be modified. - */ - -class NotModifiableCall extends FunctionCall { - NotModifiableCall() { - this.getTarget() - .hasGlobalOrStdName(["getenv", "setlocale", "localeconv", "asctime", "strerror"]) - } -} - -/* - * An expression that modifies an object. - */ - -class ObjectWrite extends Expr { - ObjectWrite() { - // the pointed object is reassigned - exists(Expr e | - e = [any(AssignExpr ae).getLValue(), any(CrementOperation co).getOperand()] and - ( - this = e.(PointerDereferenceExpr).getOperand() - or - this = e.(PointerFieldAccess).getQualifier() - ) - ) - } -} - -/** - * DF configuration for flows from a `NotModifiableCall` to a object modifications. - */ -class DFConf extends DataFlow::Configuration { - DFConf() { this = "DFConf" } - - override predicate isSource(DataFlow::Node source) { - source.asExpr() instanceof NotModifiableCall - } - - override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ObjectWrite } -} - -from DataFlow::PathNode source, DataFlow::PathNode sink -where - not isExcluded(sink.getNode().asExpr(), - Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery()) and - // the modified object comes from a call to one of the ENV functions - any(DFConf d).hasFlowPath(source, sink) -select sink.getNode(), source, sink, - "The object returned by the function " + - source.getNode().asExpr().(FunctionCall).getTarget().getName() + " should no be modified." diff --git a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md deleted file mode 100644 index bb222bfbce..0000000000 --- a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.md +++ /dev/null @@ -1,225 +0,0 @@ -# ENV31-C: Do not rely on an env pointer following an operation that may invalidate it - -This query implements the CERT-C rule ENV31-C: - -> Do not rely on an environment pointer following an operation that may invalidate it - - -## Description - -Some implementations provide a nonportable environment pointer that is valid when `main()` is called but may be invalidated by operations that modify the environment. - -The C Standard, J.5.1 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\], states - -> In a hosted environment, the main function receives a third argument, `char *envp[]`, that points to a null-terminated array of pointers to `char`, each of which points to a string that provides information about the environment for this execution of the program. - - -Consequently, under a [hosted environment](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions) supporting this common extension, it is possible to access the environment through a modified form of `main()`: - -```cpp -main(int argc, char *argv[], char *envp[]){ /* ... */ } - -``` -However, modifying the environment by any means may cause the environment memory to be reallocated, with the result that `envp` now references an incorrect location. For example, when compiled with GCC 4.8.1 and run on a 32-bit Intel GNU/Linux machine, the following code, - -```cpp -#include -#include - -extern char **environ; - -int main(int argc, const char *argv[], const char *envp[]) { - printf("environ: %p\n", environ); - printf("envp: %p\n", envp); - setenv("MY_NEW_VAR", "new_value", 1); - puts("--Added MY_NEW_VAR--"); - printf("environ: %p\n", environ); - printf("envp: %p\n", envp); - return 0; -} - -``` -yields - -```cpp -% ./envp-environ -environ: 0xbf8656ec -envp: 0xbf8656ec ---Added MY_NEW_VAR-- -environ: 0x804a008 -envp: 0xbf8656ec - -``` -It is evident from these results that the environment has been relocated as a result of the call to `setenv()`. The external variable `environ` is updated to refer to the current environment; the `envp` parameter is not. - -An environment pointer may also become invalidated by subsequent calls to `getenv().` (See [ENV34-C. Do not store pointers returned by certain functions](https://wiki.sei.cmu.edu/confluence/display/c/ENV34-C.+Do+not+store+pointers+returned+by+certain+functions) for more information.) - -## Noncompliant Code Example (POSIX) - -After a call to the POSIX `setenv()` function or to another function that modifies the environment, the `envp` pointer may no longer reference the current environment. The *Portable Operating System Interface (POSIX®), Base Specifications, Issue 7* \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\], states - -> Unanticipated results may occur if `setenv()` changes the external variable `environ`. In particular, if the optional `envp` argument to `main()` is present, it is not changed, and thus may point to an obsolete copy of the environment (as may any other copy of `environ`). - - -This noncompliant code example accesses the `envp` pointer after calling `setenv()`: - -```cpp -#include -#include - -int main(int argc, const char *argv[], const char *envp[]) { - if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { - /* Handle error */ - } - if (envp != NULL) { - for (size_t i = 0; envp[i] != NULL; ++i) { - puts(envp[i]); - } - } - return 0; -} - -``` -Because `envp` may no longer point to the current environment, this program has unanticipated behavior. - -## Compliant Solution (POSIX) - -Use `environ` in place of `envp` when defined: - -```cpp -#include -#include - -extern char **environ; - -int main(void) { - if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { - /* Handle error */ - } - if (environ != NULL) { - for (size_t i = 0; environ[i] != NULL; ++i) { - puts(environ[i]); - } - } - return 0; -} - -``` - -## Noncompliant Code Example (Windows) - -After a call to the Windows [_putenv_s()](http://msdn.microsoft.com/en-us/library/eyw7eyfw.aspx) function or to another function that modifies the environment, the `envp` pointer may no longer reference the environment. - -According to the Visual C++ reference \[[MSDN](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-MSDN)\] - -> The environment block passed to `main` and `wmain` is a "frozen" copy of the current environment. If you subsequently change the environment via a call to `_putenv` or `_wputenv`, the current environment (as returned by `getenv` / `_wgetenv` and the `_environ` / `_wenviron` variable) will change, but the block pointed to by `envp` will not change. - - -This noncompliant code example accesses the `envp` pointer after calling `_putenv_s()`: - -```cpp -#include -#include - -int main(int argc, const char *argv[], const char *envp[]) { - if (_putenv_s("MY_NEW_VAR", "new_value") != 0) { - /* Handle error */ - } - if (envp != NULL) { - for (size_t i = 0; envp[i] != NULL; ++i) { - puts(envp[i]); - } - } - return 0; -} - -``` -Because `envp` no longer points to the current environment, this program has unanticipated behavior. - -## Compliant Solution (Windows) - -This compliant solution uses the [_environ](http://msdn.microsoft.com/en-us/library/stxk41x1.aspx) variable in place of `envp`: - -```cpp -#include -#include - -_CRTIMP extern char **_environ; - -int main(int argc, const char *argv[]) { - if (_putenv_s("MY_NEW_VAR", "new_value") != 0) { - /* Handle error */ - } - if (_environ != NULL) { - for (size_t i = 0; _environ[i] != NULL; ++i) { - puts(_environ[i]); - } - } -return 0; -} - -``` - -## Compliant Solution - -This compliant solution can reduce remediation time when a large amount of noncompliant `envp` code exists. It replaces - -```cpp -int main(int argc, char *argv[], char *envp[]) { - /* ... */ -} - -``` -with - -```cpp -#if defined (_POSIX_) || defined (__USE_POSIX) - extern char **environ; - #define envp environ -#elif defined(_WIN32) - _CRTIMP extern char **_environ; - #define envp _environ -#endif - -int main(int argc, char *argv[]) { - /* ... */ -} - -``` -This compliant solution may need to be extended to support other implementations that support forms of the external variable `environ`. - -## Risk Assessment - -Using the `envp` environment pointer after the environment has been modified can result in [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). - -
Rule Severity Likelihood Remediation Cost Priority Level
ENV31-C Low Probable Medium P4 L3
- - -## Automated Detection - -
Tool Version Checker Description
Astrée 22.04 Supported
Compass/ROSE
Helix QAC 2022.2 C4991, C4992, C4993 C++4991, C++4992, C++4993
LDRA tool suite 9.7.1 118 S Fully Implemented
Parasoft C/C++test 2022.1 CERT_C-ENV31-a Do not rely on an environment pointer following an operation that may invalidate it
Polyspace Bug Finder R2022a CERT C: Rule ENV31-C Checks for environment pointer invalidated by previous operation (rule fully covered)
PRQA QA-C 9.7 4991, 4992, 4993
PRQA QA-C++ 4.4 4991, 4992, 4993
- - -## Related Vulnerabilities - -Search for vulnerabilities resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+MEM00-CPP). - -## Related Guidelines - -[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) - -
Taxonomy Taxonomy item Relationship
CERT C VOID ENV31-CPP. Do not rely on an environment pointer following an operation that may invalidate it Prior to 2018-01-12: CERT: Unspecified Relationship
- - -## Bibliography - -
\[ IEEE Std 1003.1:2013 \] XSH, System Interfaces, setenv
\[ ISO/IEC 9899:2011 \] J.5.1, "Environment Arguments"
\[ MSDN \] _environ , _wenviron , getenv , _wgetenv , _putenv_s , _wputenv_s
- - -## Implementation notes - -None - -## References - -* CERT-C: [ENV31-C: Do not rely on an environment pointer following an operation that may invalidate it](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql b/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql deleted file mode 100644 index 45e135737e..0000000000 --- a/c/cert/src/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @id c/cert/env-pointer-is-invalid-after-certain-operations - * @name ENV31-C: Do not rely on an env pointer following an operation that may invalidate it - * @description EnvironmentPointerIsInvalidAfterCertainOperations - * @kind problem - * @precision high - * @problem.severity error - * @tags external/cert/id/env31-c - * correctness - * external/cert/obligation/rule - */ - -import cpp -import codingstandards.c.cert - -/* - * Call to functions that modify the environment. - */ - -class EnvModifyingCall extends FunctionCall { - EnvModifyingCall() { this.getTarget().hasGlobalOrStdName(["setenv", "_putenv_s"]) } -} - -/* - * Function main. - */ - -class MainFunction extends Function { - MainFunction() { - hasGlobalName("main") and - getType() instanceof IntType - } -} - -from VariableAccess va, MainFunction main, EnvModifyingCall call, Parameter envp -where - not isExcluded(va, Contracts1Package::envPointerIsInvalidAfterCertainOperationsQuery()) and - // param envp exists - main.getNumberOfParameters() >= 3 and - envp = main.getParameter(2) and - va.getTarget() = envp and - va = call.getASuccessor+() -select va, - "Accessing " + va + - " following a $@ is invalid because the optional $@ argument is present in main.", call, - call.toString(), envp, envp.getName() diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql index 5b329c2545..b262f65369 100644 --- a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql @@ -1,7 +1,7 @@ /** * @id c/cert/toctou-race-conditions-while-accessing-files * @name FIO45-C: Avoid TOCTOU race conditions while accessing files - * @description Avoid TOCTOU race conditions accessing files + * @description TOCTOU race conditions when accessing files can lead to vulnerability. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql index 9a0a7b416c..84ff8d4e22 100644 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql @@ -1,7 +1,7 @@ /** * @id c/cert/use-valid-specifiers * @name FIO47-C: Use valid format strings - * @description Use valid conversion specifier + * @description Use valid conversion specifier. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql index c974949559..680a3b69bb 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql @@ -1,7 +1,7 @@ /** * @id c/cert/wrong-number-of-format-arguments * @name FIO47-C: Use correct number fo arguments - * @description Use correct number of format arguments + * @description Use correct number of format arguments. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql index 338923152b..333295b709 100644 --- a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql @@ -1,7 +1,7 @@ /** * @id c/cert/wrong-type-format-arguments * @name FIO47-C: Wrong type format arguments - * @description Wrong type format arguments + * @description Wrong type format arguments. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/src/rules/FIO47-C/check.svg b/c/cert/src/rules/FIO47-C/check.svg deleted file mode 100644 index e9573db5e7..0000000000 --- a/c/cert/src/rules/FIO47-C/check.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/c/cert/src/rules/FIO47-C/error.svg b/c/cert/src/rules/FIO47-C/error.svg deleted file mode 100644 index b897217cb9..0000000000 --- a/c/cert/src/rules/FIO47-C/error.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected deleted file mode 100644 index 91d6d9a1b4..0000000000 --- a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.expected +++ /dev/null @@ -1,20 +0,0 @@ -edges -| test.c:5:18:5:22 | c_str | test.c:8:8:8:12 | c_str | -| test.c:15:16:15:21 | call to getenv | test.c:21:9:21:12 | env1 | -| test.c:21:9:21:12 | env1 | test.c:5:18:5:22 | c_str | -| test.c:61:11:61:20 | call to localeconv | test.c:64:5:64:9 | conv4 | -| test.c:69:25:69:34 | call to localeconv | test.c:73:5:73:8 | conv | -nodes -| test.c:5:18:5:22 | c_str | semmle.label | c_str | -| test.c:8:8:8:12 | c_str | semmle.label | c_str | -| test.c:15:16:15:21 | call to getenv | semmle.label | call to getenv | -| test.c:21:9:21:12 | env1 | semmle.label | env1 | -| test.c:61:11:61:20 | call to localeconv | semmle.label | call to localeconv | -| test.c:64:5:64:9 | conv4 | semmle.label | conv4 | -| test.c:69:25:69:34 | call to localeconv | semmle.label | call to localeconv | -| test.c:73:5:73:8 | conv | semmle.label | conv | -subpaths -#select -| test.c:8:8:8:12 | c_str | test.c:15:16:15:21 | call to getenv | test.c:8:8:8:12 | c_str | The object returned by the function getenv should no be modified. | -| test.c:64:5:64:9 | conv4 | test.c:61:11:61:20 | call to localeconv | test.c:64:5:64:9 | conv4 | The object returned by the function localeconv should no be modified. | -| test.c:73:5:73:8 | conv | test.c:69:25:69:34 | call to localeconv | test.c:73:5:73:8 | conv | The object returned by the function localeconv should no be modified. | diff --git a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref b/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref deleted file mode 100644 index 9c9c06e9de..0000000000 --- a/c/cert/test/rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/ENV30-C/DoNotModifyTheReturnValueOfCertainFunctions.ql \ No newline at end of file diff --git a/c/cert/test/rules/ENV30-C/test.c b/c/cert/test/rules/ENV30-C/test.c deleted file mode 100644 index cd7c101898..0000000000 --- a/c/cert/test/rules/ENV30-C/test.c +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include - -void trstr(char *c_str, char orig, char rep) { - while (*c_str != '\0') { - if (*c_str == orig) { - *c_str = rep; // NON_COMPLIANT - } - ++c_str; - } -} - -void f1(void) { - char *env1 = getenv("TEST_ENV"); - char *copy_of_env; - copy_of_env = env1; // COMPLIANT - - if (env1 == NULL) { - } - trstr(env1, '"', '_'); -} - -void f2(void) { - const char *env2; - char *copy_of_env; - - env2 = getenv("TEST_ENV"); - if (env2 == NULL) { - } - - copy_of_env = (char *)malloc(strlen(env2) + 1); - if (copy_of_env == NULL) { - } - - strcpy(copy_of_env, env2); - trstr(copy_of_env, '"', '_'); // COMPLIANT -} - -void f3(void) { - const char *env3; - char *copy_of_env; - - env3 = getenv("TEST_ENV"); - if (env3 == NULL) { - } - - copy_of_env = strdup(env3); - if (copy_of_env == NULL) { - } - - trstr(copy_of_env, '"', '_'); // COMPLIANT - if (setenv("TEST_ENV", copy_of_env, 1) != 0) { - } -} - -void f4(void) { - struct lconv *conv4 = localeconv(); - - setlocale(LC_ALL, "C"); // COMPLIANT - conv4 = localeconv(); // COMPLIANT - - if ('\0' == conv4->decimal_point[0]) { - conv4->decimal_point = "."; // NON_COMPLIANT - } -} - -void f4alias(void) { - struct lconv *conv4 = localeconv(); - struct lconv *conv = conv4; - - if ('\0' == conv4->decimal_point[0]) { - conv->decimal_point = "."; // NON_COMPLIANT - } -} - -void f5(void) { - const struct lconv *conv5 = localeconv(); - if (conv5 == NULL) { - } - - struct lconv *copy_of_conv = (struct lconv *)malloc(sizeof(struct lconv)); - if (copy_of_conv == NULL) { - } - - memcpy(copy_of_conv, conv5, sizeof(struct lconv)); - - if ('\0' == copy_of_conv->decimal_point[0]) { - copy_of_conv->decimal_point = "."; // COMPLIANT - } - - free(copy_of_conv); -} \ No newline at end of file diff --git a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected deleted file mode 100644 index 7c42b5c817..0000000000 --- a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.expected +++ /dev/null @@ -1,6 +0,0 @@ -| test.c:23:9:23:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:15:9:15:14 | call to setenv | call to setenv | test.c:11:52:11:55 | envp | envp | -| test.c:24:26:24:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:15:9:15:14 | call to setenv | call to setenv | test.c:11:52:11:55 | envp | envp | -| test.c:25:14:25:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:15:9:15:14 | call to setenv | call to setenv | test.c:11:52:11:55 | envp | envp | -| test.c:38:9:38:12 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:30:9:30:17 | call to _putenv_s | call to _putenv_s | test.c:11:52:11:55 | envp | envp | -| test.c:39:26:39:29 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:30:9:30:17 | call to _putenv_s | call to _putenv_s | test.c:11:52:11:55 | envp | envp | -| test.c:40:14:40:17 | envp | Accessing envp following a $@ is invalid because the optional $@ argument is present in main. | test.c:30:9:30:17 | call to _putenv_s | call to _putenv_s | test.c:11:52:11:55 | envp | envp | diff --git a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref b/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref deleted file mode 100644 index 1f9a564c8e..0000000000 --- a/c/cert/test/rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/ENV31-C/EnvPointerIsInvalidAfterCertainOperations.ql \ No newline at end of file diff --git a/c/cert/test/rules/ENV31-C/test.c b/c/cert/test/rules/ENV31-C/test.c deleted file mode 100644 index 63fe9d7451..0000000000 --- a/c/cert/test/rules/ENV31-C/test.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include - -// UNIX -extern char **environ; - -// WINDOWS -extern char **_environ; -int _putenv_s(const char *varname, const char *value_string); - -int main(int argc, const char *argv[], const char *envp[]) { - - if (argc) { - // UNIX - if (setenv("MY_NEW_VAR", "new_value", 1) != 0) { - /* Handle error */ - } - if (environ != NULL) { // COMPLIANT - for (size_t i = 0; environ[i] != NULL; ++i) { - puts(environ[i]); - } - } - if (envp != NULL) { // NON_COMPLIANT - for (size_t i = 0; envp[i] != NULL; ++i) { // NON_COMPLIANT - puts(envp[i]); // NON_COMPLIANT - } - } - } else { - // WINDOWS - if (_putenv_s("MY_NEW_VAR", "new_value") != 0) { - /* Handle error */ - } - if (_environ != NULL) { // COMPLIANT - for (size_t i = 0; _environ[i] != NULL; ++i) { - puts(_environ[i]); - } - } - if (envp != NULL) { // NON_COMPLIANT - for (size_t i = 0; envp[i] != NULL; ++i) { // NON_COMPLIANT - puts(envp[i]); // NON_COMPLIANT - } - } - } - return 0; -} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll deleted file mode 100644 index b7a10673b7..0000000000 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/Contracts1.qll +++ /dev/null @@ -1,42 +0,0 @@ -//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ -import cpp -import RuleMetadata -import codingstandards.cpp.exclusions.RuleMetadata - -newtype Contracts1Query = - TDoNotModifyTheReturnValueOfCertainFunctionsQuery() or - TEnvPointerIsInvalidAfterCertainOperationsQuery() - -predicate isContracts1QueryMetadata(Query query, string queryId, string ruleId) { - query = - // `Query` instance for the `doNotModifyTheReturnValueOfCertainFunctions` query - Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery() and - queryId = - // `@id` for the `doNotModifyTheReturnValueOfCertainFunctions` query - "c/cert/do-not-modify-the-return-value-of-certain-functions" and - ruleId = "ENV30-C" - or - query = - // `Query` instance for the `envPointerIsInvalidAfterCertainOperations` query - Contracts1Package::envPointerIsInvalidAfterCertainOperationsQuery() and - queryId = - // `@id` for the `envPointerIsInvalidAfterCertainOperations` query - "c/cert/env-pointer-is-invalid-after-certain-operations" and - ruleId = "ENV31-C" -} - -module Contracts1Package { - Query doNotModifyTheReturnValueOfCertainFunctionsQuery() { - //autogenerate `Query` type - result = - // `Query` type for `doNotModifyTheReturnValueOfCertainFunctions` query - TQueryC(TContracts1PackageQuery(TDoNotModifyTheReturnValueOfCertainFunctionsQuery())) - } - - Query envPointerIsInvalidAfterCertainOperationsQuery() { - //autogenerate `Query` type - result = - // `Query` type for `envPointerIsInvalidAfterCertainOperations` query - TQueryC(TContracts1PackageQuery(TEnvPointerIsInvalidAfterCertainOperationsQuery())) - } -} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index 0c50ccffde..e2c0f9e0e6 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -5,7 +5,6 @@ import codingstandards.cpp.exclusions.RuleMetadata import Banned import Concurrency1 import Concurrency2 -import Contracts1 import IO1 import IO2 import IO3 @@ -29,7 +28,6 @@ newtype TCQuery = TBannedPackageQuery(BannedQuery q) or TConcurrency1PackageQuery(Concurrency1Query q) or TConcurrency2PackageQuery(Concurrency2Query q) or - TContracts1PackageQuery(Contracts1Query q) or TIO1PackageQuery(IO1Query q) or TIO2PackageQuery(IO2Query q) or TIO3PackageQuery(IO3Query q) or @@ -53,7 +51,6 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) { isBannedQueryMetadata(query, queryId, ruleId) or isConcurrency1QueryMetadata(query, queryId, ruleId) or isConcurrency2QueryMetadata(query, queryId, ruleId) or - isContracts1QueryMetadata(query, queryId, ruleId) or isIO1QueryMetadata(query, queryId, ruleId) or isIO2QueryMetadata(query, queryId, ruleId) or isIO3QueryMetadata(query, queryId, ruleId) or diff --git a/rule_packages/c/Contracts1.json b/rule_packages/c/Contracts1.json deleted file mode 100644 index c3f477c9c4..0000000000 --- a/rule_packages/c/Contracts1.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "CERT-C": { - "ENV30-C": { - "implementation_scope": { - "description": "None." - }, - "properties": { - "obligation": "rule" - }, - "queries": [ - { - "description": "Return value of getenv and similar functions cannot be modified.", - "kind": "path-problem", - "name": "Do not modify the return value of certain functions", - "precision": "very-high", - "severity": "error", - "short_name": "DoNotModifyTheReturnValueOfCertainFunctions", - "tags": [ - "correctness" - ] - } - ], - "title": "Do not modify the object referenced by the return value of certain functions" - }, - "ENV31-C": { - "implementation_scope": { - "description": "None." - }, - "properties": { - "obligation": "rule" - }, - "queries": [ - { - "description": "EnvironmentPointerIsInvalidAfterCertainOperations", - "kind": "problem", - "name": "Do not rely on an env pointer following an operation that may invalidate it", - "precision": "high", - "severity": "error", - "short_name": "EnvPointerIsInvalidAfterCertainOperations", - "tags": [ - "correctness" - ] - } - ], - "title": "Do not rely on an environment pointer following an operation that may invalidate it" - } - } -} \ No newline at end of file diff --git a/rule_packages/c/IO1.json b/rule_packages/c/IO1.json index e6576be906..acb27f7063 100644 --- a/rule_packages/c/IO1.json +++ b/rule_packages/c/IO1.json @@ -88,7 +88,7 @@ }, "queries": [ { - "description": "Close files when they are no longer needed", + "description": "Close files when they are no longer needed.", "kind": "problem", "name": "Close files when they are no longer needed", "precision": "very-high", @@ -111,7 +111,7 @@ }, "queries": [ { - "description": "Do not access a closed file", + "description": "Do not access a closed file.", "kind": "problem", "name": "Do not access a closed file", "precision": "high", @@ -136,7 +136,7 @@ }, "queries": [ { - "description": "A closed FILE is accessed", + "description": "A closed FILE is accessed.", "kind": "problem", "name": "The value of a pointer to a FILE shall not be used after the associated stream has been closed", "precision": "very-high", diff --git a/rule_packages/c/IO3.json b/rule_packages/c/IO3.json index dafe8e4081..d9948e0b14 100644 --- a/rule_packages/c/IO3.json +++ b/rule_packages/c/IO3.json @@ -119,7 +119,7 @@ }, "queries": [ { - "description": "The macro EOF shall only be compared with the unmodified return value from any Standard Library function capable of returning EOF", + "description": "The macro EOF shall only be compared with the unmodified return value from any Standard Library function capable of returning EOF.", "kind": "problem", "name": "The macro EOF shall only be compared with the unmodified return value from any Standard Library", "precision": "high", diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 5f099cec6b..73d046f578 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -6,7 +6,7 @@ }, "queries": [ { - "description": "Avoid TOCTOU race conditions accessing files", + "description": "TOCTOU race conditions when accessing files can lead to vulnerability.", "kind": "problem", "name": "Avoid TOCTOU race conditions while accessing files", "precision": "high", @@ -29,7 +29,7 @@ }, "queries": [ { - "description": "Use valid conversion specifier", + "description": "Use valid conversion specifier.", "kind": "problem", "name": "Use valid format strings", "precision": "high", @@ -41,7 +41,7 @@ ] }, { - "description": "Use correct number of format arguments", + "description": "Use correct number of format arguments.", "kind": "problem", "name": "Use correct number fo arguments", "precision": "high", @@ -53,7 +53,7 @@ ] }, { - "description": "Wrong type format arguments", + "description": "Wrong type format arguments.", "kind": "problem", "name": "Wrong type format arguments", "precision": "high", @@ -67,7 +67,7 @@ ], "title": "Use valid format strings", "implementation_scope": { - "description": "None" + "description": "None." } } } diff --git a/rules.csv b/rules.csv index 0373acf7c9..e3ee8951f2 100755 --- a/rules.csv +++ b/rules.csv @@ -506,8 +506,8 @@ c,CERT-C,DCL38-C,Yes,Rule,,,Use the correct syntax when declaring a flexible arr c,CERT-C,DCL39-C,Yes,Rule,,,Avoid information leakage when passing a structure across a trust boundary,,Declarations,Hard, c,CERT-C,DCL40-C,Yes,Rule,,,Do not create incompatible declarations of the same function or object,,Declarations,Hard, c,CERT-C,DCL41-C,Yes,Rule,,,Do not declare variables inside a switch statement before the first case label,,Declarations,Medium, -c,CERT-C,ENV30-C,Yes,Rule,,,Do not modify the object referenced by the return value of certain functions,RULE-21-19,Contracts1,Medium, -c,CERT-C,ENV31-C,Yes,Rule,,,Do not rely on an environment pointer following an operation that may invalidate it,RULE-21-20,Contracts1,Hard, +c,CERT-C,ENV30-C,Yes,Rule,,,Do not modify the object referenced by the return value of certain functions,RULE-21-19,Contracts,Medium, +c,CERT-C,ENV31-C,Yes,Rule,,,Do not rely on an environment pointer following an operation that may invalidate it,RULE-21-20,Contracts,Hard, c,CERT-C,ENV32-C,Yes,Rule,,,All exit handlers must return normally,,Contracts,Medium, c,CERT-C,ENV33-C,Yes,Rule,,,Do not call system(),"RULE-21-21, M18-0-3",Banned,Easy, c,CERT-C,ENV34-C,Yes,Rule,,,Do not store pointers returned by certain functions,RULE-21-20,Contracts,Medium, From 5a2a4d82e6373b7bda44e4ab9b952ddda4e49656 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Fri, 12 Aug 2022 20:36:13 +0200 Subject: [PATCH 18/31] IO4 --- scripts/generate_rules/generate_package_description.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/generate_rules/generate_package_description.py b/scripts/generate_rules/generate_package_description.py index 51fb63a97d..d3890e230f 100644 --- a/scripts/generate_rules/generate_package_description.py +++ b/scripts/generate_rules/generate_package_description.py @@ -166,10 +166,7 @@ def generate_short_name(title): "kind" : "problem", "tags" : [] } - ], - "implementation_scope": { - "description": "None." - } + ] } if not package_description: From 87e606f8cb4ff720076b6fd9497a153a22de9ced Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Fri, 12 Aug 2022 20:38:53 +0200 Subject: [PATCH 19/31] IO4 --- c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql | 2 +- .../src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql | 2 +- c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql | 2 +- .../RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql index 2acd27d08b..d1af303dc0 100644 --- a/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql +++ b/c/cert/src/rules/FIO42-C/CloseFilesWhenTheyAreNoLongerNeeded.ql @@ -1,7 +1,7 @@ /** * @id c/cert/close-files-when-they-are-no-longer-needed * @name FIO42-C: Close files when they are no longer needed - * @description Close files when they are no longer needed + * @description Close files when they are no longer needed. * @kind problem * @precision very-high * @problem.severity error diff --git a/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql b/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql index 81ae3a229a..6bc284c2c7 100644 --- a/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql +++ b/c/cert/src/rules/FIO46-C/UndefinedBehaviorAccessingAClosedFile.ql @@ -1,7 +1,7 @@ /** * @id c/cert/undefined-behavior-accessing-a-closed-file * @name FIO46-C: Do not access a closed file - * @description Do not access a closed file + * @description Do not access a closed file. * @kind problem * @precision high * @problem.severity error diff --git a/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql b/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql index 3a4be49925..78c5063ddd 100644 --- a/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql +++ b/c/misra/src/rules/RULE-22-6/FileUsedAfterClosed.ql @@ -1,7 +1,7 @@ /** * @id c/misra/file-used-after-closed * @name RULE-22-6: The value of a pointer to a FILE shall not be used after the associated stream has been closed - * @description A closed FILE is accessed + * @description A closed FILE is accessed. * @kind problem * @precision very-high * @problem.severity error diff --git a/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql b/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql index 457084f35c..22499946a0 100644 --- a/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql +++ b/c/misra/src/rules/RULE-22-7/EofShallBeComparedWithUnmodifiedReturnValues.ql @@ -2,7 +2,7 @@ * @id c/misra/eof-shall-be-compared-with-unmodified-return-values * @name RULE-22-7: The macro EOF shall only be compared with the unmodified return value from any Standard Library * @description The macro EOF shall only be compared with the unmodified return value from any - * Standard Library function capable of returning EOF + * Standard Library function capable of returning EOF. * @kind problem * @precision high * @problem.severity error From a790db4fd5816ac61b5e59740fd390ad28a5049e Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Mon, 15 Aug 2022 17:15:20 +0200 Subject: [PATCH 20/31] Fixes typos in test --- c/cert/test/rules/FIO47-C/test.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/c/cert/test/rules/FIO47-C/test.c b/c/cert/test/rules/FIO47-C/test.c index f1d26be73c..f7e0a25c4b 100644 --- a/c/cert/test/rules/FIO47-C/test.c +++ b/c/cert/test/rules/FIO47-C/test.c @@ -442,36 +442,36 @@ void test_wrong_arg_type() { printf("%hho", v_char_ptr); // NON_COMPLIANT printf("%lo", v_char_ptr); // NON_COMPLIANT printf("%llo", v_char_ptr); // NON_COMPLIANT - printf("%jo", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%jo", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%zo", v_char_ptr); // NON_COMPLIANT - printf("%to", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%to", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%u", v_char_ptr); // NON_COMPLIANT printf("%hu", v_char_ptr); // NON_COMPLIANT printf("%hhu", v_char_ptr); // NON_COMPLIANT printf("%lu", v_char_ptr); // NON_COMPLIANT printf("%llu", v_char_ptr); // NON_COMPLIANT - printf("%ju", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%ju", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%zu", v_char_ptr); // NON_COMPLIANT - printf("%tu", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%tu", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%x", v_char_ptr); // NON_COMPLIANT printf("%hx", v_char_ptr); // NON_COMPLIANT printf("%hhx", v_char_ptr); // NON_COMPLIANT printf("%lx", v_char_ptr); // NON_COMPLIANT printf("%llx", v_char_ptr); // NON_COMPLIANT - printf("%jx", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%jx", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%zx", v_char_ptr); // NON_COMPLIANT - printf("%tx", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%tx", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%X", v_char_ptr); // NON_COMPLIANT printf("%hX", v_char_ptr); // NON_COMPLIANT printf("%hhX", v_char_ptr); // NON_COMPLIANT printf("%lX", v_char_ptr); // NON_COMPLIANT printf("%llX", v_char_ptr); // NON_COMPLIANT - printf("%jX", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%jX", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%zX", v_char_ptr); // NON_COMPLIANT - printf("%tX", v_char_ptr); // NON_COMPLIANT[FALSE_POSITIVE] + printf("%tX", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%f", v_char_ptr); // NON_COMPLIANT printf("%lf", v_char_ptr); // NON_COMPLIANT From 9ae51c742d2e4652f1736847a44dbc7d3693ee1a Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Mon, 15 Aug 2022 20:45:55 +0200 Subject: [PATCH 21/31] Removed unneded implementation_scope --- rule_packages/c/IO4.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 73d046f578..4cbc999736 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -65,10 +65,7 @@ ] } ], - "title": "Use valid format strings", - "implementation_scope": { - "description": "None." - } + "title": "Use valid format strings" } } } \ No newline at end of file From 602fa12943f27844da9355306872db552e887c20 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 16 Aug 2022 02:20:45 +0200 Subject: [PATCH 22/31] Fix implementation_scope --- .../FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md | 2 +- rule_packages/c/IO4.json | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md index f4932ad2e2..884c816216 100644 --- a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md @@ -155,7 +155,7 @@ Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+D ## Implementation notes -None +The query is limited to the specific class of TOCTOU race conditions. ## References diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 4cbc999736..7582314f06 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -15,13 +15,13 @@ "tags": [ "correctness", "security" - ] + ], + "implementation_scope": { + "description": "The query is limited to the specific class of TOCTOU race conditions." + } } ], - "title": "Avoid TOCTOU race conditions while accessing files", - "implementation_scope": { - "description": "The query is limited to the specific class of TOCTOU race conditions." - } + "title": "Avoid TOCTOU race conditions while accessing files" }, "FIO47-C": { "properties": { From 1bac2841835124bc547afeddd3de6cc174f657e9 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 17 Aug 2022 20:14:16 +0200 Subject: [PATCH 23/31] Add comment on stdlib version of the query --- c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql index 333295b709..1bc67c42b8 100644 --- a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql @@ -11,10 +11,14 @@ * external/cert/obligation/rule */ -// Copy of Standard Library's query WrongTypeFormatArguments.ql import cpp import codingstandards.c.cert +/* + * This is a copy of the `WrongTypeFormatArguments.ql` query from the standard set of + * queries as of the `codeql-cli/2.6.3` tag in `github/codeql`. + */ + /** * Holds if the argument corresponding to the `pos` conversion specifier * of `ffc` is expected to have type `expected`. From 57b17fc727dff9086d829de5ce8861206246c67d Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 17 Aug 2022 20:17:05 +0200 Subject: [PATCH 24/31] Update IO4.json --- rule_packages/c/IO4.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 7582314f06..25a585fb46 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -29,7 +29,7 @@ }, "queries": [ { - "description": "Use valid conversion specifier.", + "description": "Invalid conversion specifiers leeds to undefined behavior.", "kind": "problem", "name": "Use valid format strings", "precision": "high", @@ -41,7 +41,7 @@ ] }, { - "description": "Use correct number of format arguments.", + "description": "Incorrect number of format arguments leeds to undefined behavior.", "kind": "problem", "name": "Use correct number fo arguments", "precision": "high", @@ -53,7 +53,7 @@ ] }, { - "description": "Wrong type format arguments.", + "description": "Wrong type of format arguments leeds to undefined behavior.", "kind": "problem", "name": "Wrong type format arguments", "precision": "high", @@ -68,4 +68,4 @@ "title": "Use valid format strings" } } -} \ No newline at end of file +} From 6796f6f54c369bc2a048f12e08e55880ccbe5f83 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 17 Aug 2022 20:41:26 +0200 Subject: [PATCH 25/31] Fix rule description in ql files --- c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql | 2 +- c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql | 2 +- c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql index 84ff8d4e22..97c93f31fb 100644 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql @@ -1,7 +1,7 @@ /** * @id c/cert/use-valid-specifiers * @name FIO47-C: Use valid format strings - * @description Use valid conversion specifier. + * @description Invalid conversion specifiers leeds to undefined behavior. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql index 680a3b69bb..1f0d35aeef 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql @@ -1,7 +1,7 @@ /** * @id c/cert/wrong-number-of-format-arguments * @name FIO47-C: Use correct number fo arguments - * @description Use correct number of format arguments. + * @description Incorrect number of format arguments leeds to undefined behavior. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql index 1bc67c42b8..775360e899 100644 --- a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql @@ -1,7 +1,7 @@ /** * @id c/cert/wrong-type-format-arguments * @name FIO47-C: Wrong type format arguments - * @description Wrong type format arguments. + * @description Wrong type of format arguments leeds to undefined behavior. * @kind problem * @precision high * @problem.severity error From 93a309e4da8e51d934d932bd01e7aaeb38ab1766 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 23 Aug 2022 17:59:35 +0200 Subject: [PATCH 26/31] Review fixes --- ...ToctouRaceConditionsWhileAccessingFiles.ql | 16 ++++----- .../src/rules/FIO47-C/UseValidSpecifiers.ql | 2 +- .../FIO47-C/WrongNumberOfFormatArguments.md | 2 +- .../FIO47-C/WrongNumberOfFormatArguments.ql | 2 +- .../rules/FIO47-C/WrongTypeFormatArguments.ql | 2 +- ...RaceConditionsWhileAccessingFiles.expected | 3 +- c/cert/test/rules/FIO45-C/test.c | 35 +++++++++++++++++++ rule_packages/c/IO4.json | 8 ++--- 8 files changed, 52 insertions(+), 18 deletions(-) diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql index b262f65369..17208d3b8f 100644 --- a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql @@ -19,18 +19,16 @@ import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.valuenumbering.GlobalValueNumbering /** - * A function call that opens a file as read-only - * but does not read the content of the file. + * A file opened as read-only that is never read from. */ class EmptyFOpenCall extends FOpenCall { EmptyFOpenCall() { this.isReadOnlyMode() and - // the FILE is only used as argument to close or in a NULL check - not exists(Expr x | - this != x and - DataFlow::localExprFlow(this, x) and - not closed(x) and - exists(EQExpr eq | + // FILE is only used as argument to close or in a NULL check + forall(Expr x | this != x and DataFlow::localExprFlow(this, x) | + closed(x) + or + exists(EqualityOperation eq | eq.getAnOperand() = x and eq.getAnOperand() = any(NULLMacro m).getAnInvocation().getExpr() ) ) @@ -44,5 +42,5 @@ where not fopen.isReadOnlyMode() and globalValueNumber(emptyFopen.getFilenameExpr()) = globalValueNumber(fopen.getFilenameExpr()) select emptyFopen, - "This call is trying to prevent an exsisting file to be overwritten by $@. An attacker might be able to exploit the race window between the two calls.", + "This call is trying to prevent an existing file from being overwritten by $@. An attacker might be able to exploit the race window between the two calls.", fopen, "another call" diff --git a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql index 97c93f31fb..2062cba2c4 100644 --- a/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql @@ -1,7 +1,7 @@ /** * @id c/cert/use-valid-specifiers * @name FIO47-C: Use valid format strings - * @description Invalid conversion specifiers leeds to undefined behavior. + * @description Invalid conversion specifiers leads to undefined behavior. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md index 47e8f9e038..3284f5544f 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md @@ -1,4 +1,4 @@ -# FIO47-C: Use correct number fo arguments +# FIO47-C: Use correct number of arguments This query implements the CERT-C rule FIO47-C: diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql index 1f0d35aeef..5f2d848544 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql @@ -1,6 +1,6 @@ /** * @id c/cert/wrong-number-of-format-arguments - * @name FIO47-C: Use correct number fo arguments + * @name FIO47-C: Use correct number of arguments * @description Incorrect number of format arguments leeds to undefined behavior. * @kind problem * @precision high diff --git a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql index 775360e899..66cbe409f6 100644 --- a/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql @@ -1,7 +1,7 @@ /** * @id c/cert/wrong-type-format-arguments * @name FIO47-C: Wrong type format arguments - * @description Wrong type of format arguments leeds to undefined behavior. + * @description Wrong type of format arguments leads to undefined behavior. * @kind problem * @precision high * @problem.severity error diff --git a/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected index 7bf165306e..1b2923b780 100644 --- a/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected +++ b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected @@ -1 +1,2 @@ -| test.c:4:13:4:17 | call to fopen | This call is trying to prevent an exsisting file to be overwritten by $@. An attacker might be able to exploit the race window between the two calls. | test.c:11:9:11:13 | call to fopen | another call | +| test.c:4:13:4:17 | call to fopen | This call is trying to prevent an existing file from being overwritten by $@. An attacker might be able to exploit the race window between the two calls. | test.c:11:9:11:13 | call to fopen | another call | +| test.c:88:13:88:17 | call to fopen | This call is trying to prevent an existing file from being overwritten by $@. An attacker might be able to exploit the race window between the two calls. | test.c:95:9:95:13 | call to fopen | another call | diff --git a/c/cert/test/rules/FIO45-C/test.c b/c/cert/test/rules/FIO45-C/test.c index bbe8ad7977..b70aade831 100644 --- a/c/cert/test/rules/FIO45-C/test.c +++ b/c/cert/test/rules/FIO45-C/test.c @@ -83,3 +83,38 @@ int f4(char *filename, int flags) { /* f is the expected regular open file */ return f; } + +void f5(const char *file) { + FILE *f = fopen("file", "r"); // NON_COMPLIANT + if (NULL != f) { + /* File exists, handle error */ + } else { + if (fclose(f) == EOF) { + /* Handle error */ + } + f = fopen("file", "w"); + if (NULL == f) { + /* Handle error */ + } + + /* Write to file */ + if (fclose(f) == EOF) { + /* Handle error */ + } + } +} + +void f6(const char *file) { + int readChar; + FILE *f = fopen(file, "r"); // COMPLIANT + // the file is accessed + if (NULL != f) { + /* File exists, handle error */ + } else { + // read file + readChar = fgetc(f); + printf("%c", readChar); + } + + f = fopen(file, "w"); +} \ No newline at end of file diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 25a585fb46..748b0ae655 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -29,7 +29,7 @@ }, "queries": [ { - "description": "Invalid conversion specifiers leeds to undefined behavior.", + "description": "Invalid conversion specifiers leads to undefined behavior.", "kind": "problem", "name": "Use valid format strings", "precision": "high", @@ -41,9 +41,9 @@ ] }, { - "description": "Incorrect number of format arguments leeds to undefined behavior.", + "description": "Incorrect number of format arguments leads to undefined behavior.", "kind": "problem", - "name": "Use correct number fo arguments", + "name": "Use correct number of arguments", "precision": "high", "severity": "error", "short_name": "WrongNumberOfFormatArguments", @@ -53,7 +53,7 @@ ] }, { - "description": "Wrong type of format arguments leeds to undefined behavior.", + "description": "Wrong type of format arguments leads to undefined behavior.", "kind": "problem", "name": "Wrong type format arguments", "precision": "high", From 68217c7c09d6e5a806d9ccfe808c8c2b36d5acad Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 23 Aug 2022 18:52:11 +0200 Subject: [PATCH 27/31] Fixes for review --- .../FIO47-C/WrongNumberOfFormatArguments.ql | 6 +- .../FIO47-C/WrongTypeFormatArguments.expected | 182 +++++++++--------- c/cert/test/rules/FIO47-C/test.c | 43 +++-- 3 files changed, 123 insertions(+), 108 deletions(-) diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql index 5f2d848544..f944b89656 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql @@ -11,7 +11,11 @@ * external/cert/obligation/rule */ -// Copy of Standard Library's query WrongNumberOfFormatArguments.ql +/* + * This is a copy of the `WrongNumberOfFormatArguments.ql` query from the standard set of + * queries as of the `codeql-cli/2.6.3` tag in `github/codeql`. + */ + import cpp import codingstandards.c.cert diff --git a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected index eb4161b5c3..bca4bd8128 100644 --- a/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected @@ -1,91 +1,91 @@ -| test.c:375:17:375:30 | v_intmax_t_ptr | This argument should be of type 'int *' but is of type 'signed long *' | -| test.c:376:17:376:28 | v_size_t_ptr | This argument should be of type 'int *' but is of type 'unsigned long *' | -| test.c:377:17:377:31 | v_ptrdiff_t_ptr | This argument should be of type 'int *' but is of type 'long *' | -| test.c:414:17:414:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:418:18:418:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:422:16:422:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:423:17:423:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:424:18:424:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:425:17:425:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | -| test.c:426:18:426:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | -| test.c:428:17:428:26 | v_char_ptr | This argument should be of type 'ssize_t' but is of type 'char *' | -| test.c:429:17:429:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | -| test.c:431:16:431:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:432:17:432:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:433:18:433:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | -| test.c:434:17:434:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | -| test.c:435:18:435:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | -| test.c:437:17:437:26 | v_char_ptr | This argument should be of type 'ssize_t' but is of type 'char *' | -| test.c:438:17:438:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | -| test.c:440:16:440:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:441:17:441:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:442:18:442:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:443:17:443:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | -| test.c:444:18:444:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | -| test.c:446:17:446:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | -| test.c:449:16:449:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:450:17:450:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:451:18:451:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:452:17:452:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | -| test.c:453:18:453:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | -| test.c:455:17:455:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | -| test.c:458:16:458:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:459:17:459:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:460:18:460:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:461:17:461:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | -| test.c:462:18:462:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | -| test.c:464:17:464:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | -| test.c:467:16:467:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:468:17:468:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:469:18:469:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | -| test.c:470:17:470:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | -| test.c:471:18:471:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | -| test.c:473:17:473:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | -| test.c:476:16:476:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:477:17:477:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:478:18:478:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:479:17:479:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:481:16:481:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:482:17:482:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:483:18:483:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:484:17:484:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:486:16:486:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:487:17:487:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:488:18:488:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:489:17:489:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:491:16:491:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:492:17:492:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:493:18:493:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:494:17:494:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:496:16:496:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:497:17:497:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:498:18:498:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:499:17:499:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:501:16:501:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:502:17:502:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:503:18:503:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:504:17:504:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:506:16:506:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:507:17:507:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:508:18:508:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:509:17:509:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:511:16:511:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:512:17:512:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | -| test.c:513:18:513:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:514:17:514:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | -| test.c:516:16:516:25 | v_char_ptr | This argument should be of type 'char' but is of type 'char *' | -| test.c:517:17:517:26 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | -| test.c:519:16:519:20 | v_int | This argument should be of type 'char *' but is of type 'int' | -| test.c:520:17:520:21 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | -| test.c:522:16:522:20 | v_int | This argument should be of type 'void *' but is of type 'int' | -| test.c:524:16:524:20 | v_int | This argument should be of type 'int *' but is of type 'int' | -| test.c:525:17:525:21 | v_int | This argument should be of type 'short *' but is of type 'int' | -| test.c:526:18:526:22 | v_int | This argument should be of type 'char *' but is of type 'int' | -| test.c:527:17:527:21 | v_int | This argument should be of type 'long *' but is of type 'int' | -| test.c:527:17:527:21 | v_int | This argument should be of type 'signed long *' but is of type 'int' | -| test.c:528:18:528:22 | v_int | This argument should be of type 'long long *' but is of type 'int' | -| test.c:529:17:529:21 | v_int | This argument should be of type 'int *' but is of type 'int' | -| test.c:530:17:530:21 | v_int | This argument should be of type 'int *' but is of type 'int' | -| test.c:531:17:531:21 | v_int | This argument should be of type 'int *' but is of type 'int' | -| test.c:533:16:533:25 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | -| test.c:535:16:535:20 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | +| test.c:376:17:376:30 | v_intmax_t_ptr | This argument should be of type 'int *' but is of type 'signed long *' | +| test.c:378:17:378:28 | v_size_t_ptr | This argument should be of type 'int *' but is of type 'unsigned long *' | +| test.c:380:17:380:31 | v_ptrdiff_t_ptr | This argument should be of type 'int *' but is of type 'long *' | +| test.c:417:17:417:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:421:18:421:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:425:16:425:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:426:17:426:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:427:18:427:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:428:17:428:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:429:18:429:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:431:17:431:26 | v_char_ptr | This argument should be of type 'ssize_t' but is of type 'char *' | +| test.c:432:17:432:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:434:16:434:25 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:435:17:435:26 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:436:18:436:27 | v_char_ptr | This argument should be of type 'int' but is of type 'char *' | +| test.c:437:17:437:26 | v_char_ptr | This argument should be of type 'long' but is of type 'char *' | +| test.c:438:18:438:27 | v_char_ptr | This argument should be of type 'long long' but is of type 'char *' | +| test.c:440:17:440:26 | v_char_ptr | This argument should be of type 'ssize_t' but is of type 'char *' | +| test.c:441:17:441:26 | v_char_ptr | This argument should be of type 'ptrdiff_t' but is of type 'char *' | +| test.c:443:16:443:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:444:17:444:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:445:18:445:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:446:17:446:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:447:18:447:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:450:17:450:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:454:16:454:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:455:17:455:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:456:18:456:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:457:17:457:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:458:18:458:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:461:17:461:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:465:16:465:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:466:17:466:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:467:18:467:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:468:17:468:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:469:18:469:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:472:17:472:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:476:16:476:25 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:477:17:477:26 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:478:18:478:27 | v_char_ptr | This argument should be of type 'unsigned int' but is of type 'char *' | +| test.c:479:17:479:26 | v_char_ptr | This argument should be of type 'unsigned long' but is of type 'char *' | +| test.c:480:18:480:27 | v_char_ptr | This argument should be of type 'unsigned long long' but is of type 'char *' | +| test.c:483:17:483:26 | v_char_ptr | This argument should be of type 'size_t' but is of type 'char *' | +| test.c:487:16:487:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:488:17:488:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:489:18:489:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:490:17:490:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:492:16:492:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:493:17:493:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:494:18:494:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:495:17:495:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:497:16:497:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:498:17:498:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:499:18:499:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:500:17:500:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:502:16:502:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:503:17:503:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:504:18:504:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:505:17:505:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:507:16:507:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:508:17:508:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:509:18:509:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:510:17:510:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:512:16:512:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:513:17:513:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:514:18:514:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:515:17:515:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:517:16:517:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:518:17:518:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:519:18:519:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:520:17:520:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:522:16:522:25 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:523:17:523:26 | v_char_ptr | This argument should be of type 'double' but is of type 'char *' | +| test.c:524:18:524:27 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:525:17:525:26 | v_char_ptr | This argument should be of type 'long double' but is of type 'char *' | +| test.c:527:16:527:25 | v_char_ptr | This argument should be of type 'char' but is of type 'char *' | +| test.c:528:17:528:26 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:530:16:530:20 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:531:17:531:21 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | +| test.c:533:16:533:20 | v_int | This argument should be of type 'void *' but is of type 'int' | +| test.c:535:16:535:20 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:536:17:536:21 | v_int | This argument should be of type 'short *' but is of type 'int' | +| test.c:537:18:537:22 | v_int | This argument should be of type 'char *' but is of type 'int' | +| test.c:538:17:538:21 | v_int | This argument should be of type 'long *' but is of type 'int' | +| test.c:538:17:538:21 | v_int | This argument should be of type 'signed long *' but is of type 'int' | +| test.c:539:18:539:22 | v_int | This argument should be of type 'long long *' but is of type 'int' | +| test.c:540:17:540:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:541:17:541:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:542:17:542:21 | v_int | This argument should be of type 'int *' but is of type 'int' | +| test.c:544:16:544:25 | v_char_ptr | This argument should be of type 'wchar_t' but is of type 'char *' | +| test.c:546:16:546:20 | v_int | This argument should be of type 'wchar_t *' but is of type 'int' | diff --git a/c/cert/test/rules/FIO47-C/test.c b/c/cert/test/rules/FIO47-C/test.c index f7e0a25c4b..2ae9e02b2f 100644 --- a/c/cert/test/rules/FIO47-C/test.c +++ b/c/cert/test/rules/FIO47-C/test.c @@ -372,10 +372,13 @@ void test_incompatible_length() { printf("%hhn", v_char_ptr); // COMPLIANT printf("%ln", v_long_ptr); // COMPLIANT printf("%lln", v_long_long_ptr); // COMPLIANT - printf("%jn", v_intmax_t_ptr); // COMPLIANT[FALSE_POSITIVE] - printf("%zn", v_size_t_ptr); // COMPLIANT[FALSE_POSITIVE] - printf("%tn", v_ptrdiff_t_ptr); // COMPLIANT[FALSE_POSITIVE] - printf("%Ln", v_long_long_ptr); // NON_COMPLIANT + // FP from WrongTypeFormatArguments.ql + printf("%jn", v_intmax_t_ptr); // COMPLIANT[FALSE_POSITIVE] + // FP from WrongTypeFormatArguments.ql + printf("%zn", v_size_t_ptr); // COMPLIANT[FALSE_POSITIVE] + // FP from WrongTypeFormatArguments.ql + printf("%tn", v_ptrdiff_t_ptr); // COMPLIANT[FALSE_POSITIVE] + printf("%Ln", v_long_long_ptr); // NON_COMPLIANT printf("%hC", v_wint_t); // NON_COMPLIANT printf("%hhC", v_wint_t); // NON_COMPLIANT @@ -442,36 +445,44 @@ void test_wrong_arg_type() { printf("%hho", v_char_ptr); // NON_COMPLIANT printf("%lo", v_char_ptr); // NON_COMPLIANT printf("%llo", v_char_ptr); // NON_COMPLIANT - printf("%jo", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%zo", v_char_ptr); // NON_COMPLIANT - printf("%to", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + // FP from WrongTypeFormatArguments.ql + printf("%jo", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%zo", v_char_ptr); // NON_COMPLIANT + // FP from WrongTypeFormatArguments.ql + printf("%to", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%u", v_char_ptr); // NON_COMPLIANT printf("%hu", v_char_ptr); // NON_COMPLIANT printf("%hhu", v_char_ptr); // NON_COMPLIANT printf("%lu", v_char_ptr); // NON_COMPLIANT printf("%llu", v_char_ptr); // NON_COMPLIANT - printf("%ju", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%zu", v_char_ptr); // NON_COMPLIANT - printf("%tu", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + // FP from WrongTypeFormatArguments.ql + printf("%ju", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%zu", v_char_ptr); // NON_COMPLIANT + // FP from WrongTypeFormatArguments.ql + printf("%tu", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%x", v_char_ptr); // NON_COMPLIANT printf("%hx", v_char_ptr); // NON_COMPLIANT printf("%hhx", v_char_ptr); // NON_COMPLIANT printf("%lx", v_char_ptr); // NON_COMPLIANT printf("%llx", v_char_ptr); // NON_COMPLIANT - printf("%jx", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%zx", v_char_ptr); // NON_COMPLIANT - printf("%tx", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + // FP from WrongTypeFormatArguments.ql + printf("%jx", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%zx", v_char_ptr); // NON_COMPLIANT + // FP from WrongTypeFormatArguments.ql + printf("%tx", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%X", v_char_ptr); // NON_COMPLIANT printf("%hX", v_char_ptr); // NON_COMPLIANT printf("%hhX", v_char_ptr); // NON_COMPLIANT printf("%lX", v_char_ptr); // NON_COMPLIANT printf("%llX", v_char_ptr); // NON_COMPLIANT - printf("%jX", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] - printf("%zX", v_char_ptr); // NON_COMPLIANT - printf("%tX", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + // FP from WrongTypeFormatArguments.ql + printf("%jX", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] + printf("%zX", v_char_ptr); // NON_COMPLIANT + // FP from WrongTypeFormatArguments.ql + printf("%tX", v_char_ptr); // NON_COMPLIANT[FALSE_NEGATIVE] printf("%f", v_char_ptr); // NON_COMPLIANT printf("%lf", v_char_ptr); // NON_COMPLIANT From c96f4c141c177b7c02c0e13c3a5af8b09c11ba2b Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 23 Aug 2022 18:54:50 +0200 Subject: [PATCH 28/31] typo --- c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql index f944b89656..a8b9e9fbac 100644 --- a/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql @@ -1,7 +1,7 @@ /** * @id c/cert/wrong-number-of-format-arguments * @name FIO47-C: Use correct number of arguments - * @description Incorrect number of format arguments leeds to undefined behavior. + * @description Incorrect number of format arguments leads to undefined behavior. * @kind problem * @precision high * @problem.severity error From ce83546b8295a1ccda8a0d268153ccad00d6011c Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Tue, 23 Aug 2022 19:44:01 +0200 Subject: [PATCH 29/31] Fix expected files --- .../rules/FIO47-C/UseValidSpecifiers.expected | 50 +++++++++---------- .../WrongNumberOfFormatArguments.expected | 4 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected index 5367f2ce51..50b55365e2 100644 --- a/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected @@ -104,28 +104,28 @@ | test.c:367:10:367:14 | %zp | The conversion specifier 'p' is not compatible with length 'z' | | test.c:368:10:368:14 | %tp | The conversion specifier 'p' is not compatible with length 't' | | test.c:369:10:369:14 | %Lp | The conversion specifier 'p' is not compatible with length 'L' | -| test.c:378:10:378:14 | %Ln | The conversion specifier 'n' is not compatible with length 'L' | -| test.c:380:10:380:14 | %hC | The conversion specifier 'C' is not compatible with length 'h' | -| test.c:381:10:381:15 | %hhC | The conversion specifier 'C' is not compatible with length 'hh' | -| test.c:382:10:382:14 | %lC | The conversion specifier 'C' is not compatible with length 'l' | -| test.c:383:10:383:15 | %llC | The conversion specifier 'C' is not compatible with length 'll' | -| test.c:384:10:384:14 | %jC | The conversion specifier 'C' is not compatible with length 'j' | -| test.c:385:10:385:14 | %zC | The conversion specifier 'C' is not compatible with length 'z' | -| test.c:386:10:386:14 | %tC | The conversion specifier 'C' is not compatible with length 't' | -| test.c:387:10:387:14 | %LC | The conversion specifier 'C' is not compatible with length 'L' | -| test.c:389:10:389:14 | %hS | The conversion specifier 'S' is not compatible with length 'h' | -| test.c:390:10:390:15 | %hhS | The conversion specifier 'S' is not compatible with length 'hh' | -| test.c:391:10:391:14 | %lS | The conversion specifier 'S' is not compatible with length 'l' | -| test.c:392:10:392:15 | %llS | The conversion specifier 'S' is not compatible with length 'll' | -| test.c:393:10:393:14 | %jS | The conversion specifier 'S' is not compatible with length 'j' | -| test.c:394:10:394:14 | %zS | The conversion specifier 'S' is not compatible with length 'z' | -| test.c:395:10:395:14 | %tS | The conversion specifier 'S' is not compatible with length 't' | -| test.c:396:10:396:14 | %LS | The conversion specifier 'S' is not compatible with length 'L' | -| test.c:398:10:398:14 | %h% | The conversion specifier '%h%' is not valid. | -| test.c:399:10:399:15 | %hh% | The conversion specifier '%hh%' is not valid. | -| test.c:400:10:400:14 | %l% | The conversion specifier '%l%' is not valid. | -| test.c:401:10:401:15 | %ll% | The conversion specifier '%ll%' is not valid. | -| test.c:402:10:402:14 | %j% | The conversion specifier '%j%' is not valid. | -| test.c:403:10:403:14 | %z% | The conversion specifier '%z%' is not valid. | -| test.c:404:10:404:14 | %t% | The conversion specifier '%t%' is not valid. | -| test.c:405:10:405:14 | %L% | The conversion specifier '%L%' is not valid. | +| test.c:381:10:381:14 | %Ln | The conversion specifier 'n' is not compatible with length 'L' | +| test.c:383:10:383:14 | %hC | The conversion specifier 'C' is not compatible with length 'h' | +| test.c:384:10:384:15 | %hhC | The conversion specifier 'C' is not compatible with length 'hh' | +| test.c:385:10:385:14 | %lC | The conversion specifier 'C' is not compatible with length 'l' | +| test.c:386:10:386:15 | %llC | The conversion specifier 'C' is not compatible with length 'll' | +| test.c:387:10:387:14 | %jC | The conversion specifier 'C' is not compatible with length 'j' | +| test.c:388:10:388:14 | %zC | The conversion specifier 'C' is not compatible with length 'z' | +| test.c:389:10:389:14 | %tC | The conversion specifier 'C' is not compatible with length 't' | +| test.c:390:10:390:14 | %LC | The conversion specifier 'C' is not compatible with length 'L' | +| test.c:392:10:392:14 | %hS | The conversion specifier 'S' is not compatible with length 'h' | +| test.c:393:10:393:15 | %hhS | The conversion specifier 'S' is not compatible with length 'hh' | +| test.c:394:10:394:14 | %lS | The conversion specifier 'S' is not compatible with length 'l' | +| test.c:395:10:395:15 | %llS | The conversion specifier 'S' is not compatible with length 'll' | +| test.c:396:10:396:14 | %jS | The conversion specifier 'S' is not compatible with length 'j' | +| test.c:397:10:397:14 | %zS | The conversion specifier 'S' is not compatible with length 'z' | +| test.c:398:10:398:14 | %tS | The conversion specifier 'S' is not compatible with length 't' | +| test.c:399:10:399:14 | %LS | The conversion specifier 'S' is not compatible with length 'L' | +| test.c:401:10:401:14 | %h% | The conversion specifier '%h%' is not valid. | +| test.c:402:10:402:15 | %hh% | The conversion specifier '%hh%' is not valid. | +| test.c:403:10:403:14 | %l% | The conversion specifier '%l%' is not valid. | +| test.c:404:10:404:15 | %ll% | The conversion specifier '%ll%' is not valid. | +| test.c:405:10:405:14 | %j% | The conversion specifier '%j%' is not valid. | +| test.c:406:10:406:14 | %z% | The conversion specifier '%z%' is not valid. | +| test.c:407:10:407:14 | %t% | The conversion specifier '%t%' is not valid. | +| test.c:408:10:408:14 | %L% | The conversion specifier '%L%' is not valid. | diff --git a/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected index 1ea0fa4369..81b107c83a 100644 --- a/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected +++ b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected @@ -1,2 +1,2 @@ -| test.c:540:3:540:8 | call to printf | Format expects 2 arguments but given 1 | -| test.c:543:3:543:8 | call to printf | Format expects 2 arguments but given 1 | +| test.c:551:3:551:8 | call to printf | Format expects 2 arguments but given 1 | +| test.c:554:3:554:8 | call to printf | Format expects 2 arguments but given 1 | From f82206d8621426ef1fd420dd85fc0cb1cdf4ea82 Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 24 Aug 2022 00:30:37 +0200 Subject: [PATCH 30/31] use fcloseCall intead of closed that is overapproximated --- .../FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql index 17208d3b8f..b02ce2f58d 100644 --- a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql @@ -14,7 +14,6 @@ import cpp import codingstandards.c.cert import codingstandards.cpp.standardlibrary.FileAccess -import codingstandards.cpp.ReadErrorsAndEOF import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.valuenumbering.GlobalValueNumbering @@ -26,11 +25,9 @@ class EmptyFOpenCall extends FOpenCall { this.isReadOnlyMode() and // FILE is only used as argument to close or in a NULL check forall(Expr x | this != x and DataFlow::localExprFlow(this, x) | - closed(x) + fcloseCall(_, x) or - exists(EqualityOperation eq | - eq.getAnOperand() = x and eq.getAnOperand() = any(NULLMacro m).getAnInvocation().getExpr() - ) + exists(EqualityOperation eq | eq.getAnOperand() = x and eq.getAnOperand() = any(NULL n)) ) } } From 8c452d6587dbcd261339217a90ce90382fa8cd3a Mon Sep 17 00:00:00 2001 From: Mauro Baluda Date: Wed, 24 Aug 2022 20:45:48 +0200 Subject: [PATCH 31/31] Improved FIO45-C implementation scope --- .../rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md | 2 +- rule_packages/c/IO4.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md index 884c816216..9a5c09d486 100644 --- a/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.md @@ -155,7 +155,7 @@ Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+D ## Implementation notes -The query is limited to the specific class of TOCTOU race conditions. +The query is limited to the specific class of TOCTOU race conditions that derives from the incorrectuse of `fopen` to check the existence of a file. ## References diff --git a/rule_packages/c/IO4.json b/rule_packages/c/IO4.json index 748b0ae655..0873d2707b 100644 --- a/rule_packages/c/IO4.json +++ b/rule_packages/c/IO4.json @@ -17,7 +17,7 @@ "security" ], "implementation_scope": { - "description": "The query is limited to the specific class of TOCTOU race conditions." + "description": "The query is limited to the specific class of TOCTOU race conditions that derives from the incorrectuse of `fopen` to check the existence of a file." } } ],