diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1eda872a06..170479d5ca 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..9a5c09d486 --- /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 + +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 + +* 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..b02ce2f58d --- /dev/null +++ b/c/cert/src/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.ql @@ -0,0 +1,43 @@ +/** + * @id c/cert/toctou-race-conditions-while-accessing-files + * @name FIO45-C: Avoid TOCTOU race conditions while accessing files + * @description TOCTOU race conditions when accessing files can lead to vulnerability. + * @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 semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** + * A file opened as read-only that is never read from. + */ +class EmptyFOpenCall extends FOpenCall { + EmptyFOpenCall() { + 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) | + fcloseCall(_, x) + or + exists(EqualityOperation eq | eq.getAnOperand() = x and eq.getAnOperand() = any(NULL n)) + ) + } +} + +// 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 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.md b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.md new file mode 100644 index 0000000000..3aa0ac89e7 --- /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 ✓ 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 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.20 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..2062cba2c4 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/UseValidSpecifiers.ql @@ -0,0 +1,58 @@ +/** + * @id c/cert/use-valid-specifiers + * @name FIO47-C: Use valid format strings + * @description Invalid conversion specifiers leads to undefined behavior. + * @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", "C", "S", "%"] and result = ["'", "#", "0"] + or + specChar = ["n"] and result = ["'", "-", "+", " ", "#", "0"] +} + +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 +where + not isExcluded(x, IO4Package::useValidSpecifiersQuery()) and + message = "The conversion specifier '" + x + "' is not valid." and + not x.specsAreKnown() + or + 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 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..3284f5544f --- /dev/null +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.md @@ -0,0 +1,152 @@ +# FIO47-C: Use correct number of 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 ✓ 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 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.20 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..a8b9e9fbac --- /dev/null +++ b/c/cert/src/rules/FIO47-C/WrongNumberOfFormatArguments.ql @@ -0,0 +1,30 @@ +/** + * @id c/cert/wrong-number-of-format-arguments + * @name FIO47-C: Use correct number of arguments + * @description Incorrect number of format arguments leads to undefined behavior. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/fio47-c + * correctness + * security + * external/cert/obligation/rule + */ + +/* + * 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 + +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..deaef01918 --- /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 ✓ 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 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.20 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..66cbe409f6 --- /dev/null +++ b/c/cert/src/rules/FIO47-C/WrongTypeFormatArguments.ql @@ -0,0 +1,181 @@ +/** + * @id c/cert/wrong-type-format-arguments + * @name FIO47-C: Wrong type format arguments + * @description Wrong type of format arguments leads to undefined behavior. + * @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 + +/* + * 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`. + */ +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/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected new file mode 100644 index 0000000000..1b2923b780 --- /dev/null +++ b/c/cert/test/rules/FIO45-C/ToctouRaceConditionsWhileAccessingFiles.expected @@ -0,0 +1,2 @@ +| 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/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/c/cert/test/rules/FIO45-C/test.c b/c/cert/test/rules/FIO45-C/test.c new file mode 100644 index 0000000000..b70aade831 --- /dev/null +++ b/c/cert/test/rules/FIO45-C/test.c @@ -0,0 +1,120 @@ +#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; +} + +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/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected new file mode 100644 index 0000000000..50b55365e2 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/UseValidSpecifiers.expected @@ -0,0 +1,131 @@ +| 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: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/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..81b107c83a --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongNumberOfFormatArguments.expected @@ -0,0 +1,2 @@ +| 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 | 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..bca4bd8128 --- /dev/null +++ b/c/cert/test/rules/FIO47-C/WrongTypeFormatArguments.expected @@ -0,0 +1,91 @@ +| 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/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..2ae9e02b2f --- /dev/null +++ b/c/cert/test/rules/FIO47-C/test.c @@ -0,0 +1,555 @@ +#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"; + 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", 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", 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 + // 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 + 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() { + // 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 + // 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 + // 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 + // 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 + // 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 + 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 + + 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 new file mode 100644 index 0000000000..a1c3978fc4 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/IO4.qll @@ -0,0 +1,74 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype IO4Query = + TToctouRaceConditionsWhileAccessingFilesQuery() or + TUseValidSpecifiersQuery() or + TWrongNumberOfFormatArgumentsQuery() or + TWrongTypeFormatArgumentsQuery() + +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 `useValidSpecifiers` query + IO4Package::useValidSpecifiersQuery() and + queryId = + // `@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" +} + +module IO4Package { + Query toctouRaceConditionsWhileAccessingFilesQuery() { + //autogenerate `Query` type + result = + // `Query` type for `toctouRaceConditionsWhileAccessingFiles` query + TQueryC(TIO4PackageQuery(TToctouRaceConditionsWhileAccessingFilesQuery())) + } + + 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())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index 481c399a61..c271b218dc 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -12,6 +12,7 @@ import Expressions import IO1 import IO2 import IO3 +import IO4 import Misc import Pointers1 import Pointers2 @@ -39,6 +40,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 TPointers2PackageQuery(Pointers2Query q) or @@ -66,6 +68,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 isPointers2QueryMetadata(query, queryId, ruleId) or 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 new file mode 100644 index 0000000000..0873d2707b --- /dev/null +++ b/rule_packages/c/IO4.json @@ -0,0 +1,71 @@ +{ + "CERT-C": { + "FIO45-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "TOCTOU race conditions when accessing files can lead to vulnerability.", + "kind": "problem", + "name": "Avoid TOCTOU race conditions while accessing files", + "precision": "high", + "severity": "error", + "short_name": "ToctouRaceConditionsWhileAccessingFiles", + "tags": [ + "correctness", + "security" + ], + "implementation_scope": { + "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." + } + } + ], + "title": "Avoid TOCTOU race conditions while accessing files" + }, + "FIO47-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Invalid conversion specifiers leads to undefined behavior.", + "kind": "problem", + "name": "Use valid format strings", + "precision": "high", + "severity": "error", + "short_name": "UseValidSpecifiers", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Incorrect number of format arguments leads to undefined behavior.", + "kind": "problem", + "name": "Use correct number of arguments", + "precision": "high", + "severity": "error", + "short_name": "WrongNumberOfFormatArguments", + "tags": [ + "correctness", + "security" + ] + }, + { + "description": "Wrong type of format arguments leads to undefined behavior.", + "kind": "problem", + "name": "Wrong type format arguments", + "precision": "high", + "severity": "error", + "short_name": "WrongTypeFormatArguments", + "tags": [ + "correctness", + "security" + ] + } + ], + "title": "Use valid format strings" + } + } +} diff --git a/rules.csv b/rules.csv index 241e2f157e..02191e5626 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, diff --git a/scripts/help/cert-help-extraction.py b/scripts/help/cert-help-extraction.py index c845764448..6bd1abccd5 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()