diff --git a/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.md b/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.md new file mode 100644 index 0000000000..b6663872bf --- /dev/null +++ b/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.md @@ -0,0 +1,243 @@ +# EXP37-C: Pass the correct number of arguments to the POSIX open function. + +This query implements the CERT-C rule EXP37-C: + +> Call functions with the correct number and type of arguments + + +## Description + +Do not call a function with the wrong number or type of arguments. + +The C Standard identifies five distinct situations in which [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) (UB) may arise as a result of invoking a function using a declaration that is incompatible with its definition or by supplying incorrect types or numbers of arguments: + +
UB Description
26 A pointer is used to call a function whose type is not compatible with the referenced type (6.3.2.3).
38 For a call to a function without a function prototype in scope, the number of arguments does not equal the number of parameters (6.5.2.2).
39 For a call to a function without a function prototype in scope where the function is defined with a function prototype, either the prototype ends with an ellipsis or the types of the arguments after promotion are not compatible with the types of the parameters (6.5.2.2).
40 For a call to a function without a function prototype in scope where the function is not defined with a function prototype, the types of the arguments after promotion are not compatible with those of the parameters after promotion (with certain exceptions) (6.5.2.2).
41 A function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function (6.5.2.2).
+Functions that are appropriately declared (as in [DCL40-C. Do not create incompatible declarations of the same function or object](https://wiki.sei.cmu.edu/confluence/display/c/DCL40-C.+Do+not+create+incompatible+declarations+of+the+same+function+or+object)) will typically generate a compiler diagnostic message if they are supplied with the wrong number or types of arguments. However, there are cases in which supplying the incorrect arguments to a function will, at best, generate compiler [warnings](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-warning). Although such warnings should be resolved, they do not prevent program compilation. (See [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels).) + + +## Noncompliant Code Example + +The header `` provides type-generic macros for math functions. Although most functions from the `` header have a complex counterpart in ``, several functions do not. Calling any of the following type-generic functions with complex values is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +**Functions That Should Not Be Called with Complex Values** + +
atan2() erf() fdim() fmin() ilogb() llround() logb() nextafter() rint() tgamma()
cbrt() erfc() floor() fmod() ldexp() log10() lrint() nexttoward() round() trunc()
ceil() exp2() fma() frexp() lgamma() log1p() lround() remainder() scalbn()
copysign() expm1() fmax() hypot() llrint() log2() nearbyint() remquo() scalbln()
+This noncompliant code example attempts to take the base-2 logarithm of a complex number, resulting in undefined behavior: + + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log2(c); +} +``` + +## Compliant Solution (Complex Number) + +If the `clog2()` function is not available for an implementation as an extension, the programmer can take the base-2 logarithm of a complex number, using `log()` instead of `log2()`, because `log()` can be used on complex arguments, as shown in this compliant solution: + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log(c)/log(2); +} +``` + +## Compliant Solution (Real Number) + +The programmer can use this compliant solution if the intent is to take the base-2 logarithm of the real part of the complex number: + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log2(creal(c)); +} +``` + +## Noncompliant Code Example + +In this noncompliant example, the C standard library function `strchr()` is called through the function pointer `fp` declared with a prototype with incorrectly typed arguments. According to the C Standard, 6.3.2.3, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] + +> A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined. + + +See [undefined behavior 26.](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_26) + +```cpp +#include +#include + +char *(*fp)(); + +int main(void) { + const char *c; + fp = strchr; + c = fp('e', "Hello"); + printf("%s\n", c); + return 0; +} +``` + +## Compliant Solution + +In this compliant solution, the function pointer `fp`, which points to the C standard library function `strchr()`, is declared with the correct parameters and is invoked with the correct number and type of arguments: + +```cpp +#include +#include + +char *(*fp)(const char *, int); + +int main(void) { + const char *c; + fp = strchr; + c = fp("Hello",'e'); + printf("%s\n", c); + return 0; +} + +``` + +## Noncompliant Code Example + +In this noncompliant example, the function `f()` is defined to take an argument of type `long` but `f()` is called from another file with an argument of type `int`: + +```cpp +/* In another source file */ +long f(long x) { + return x < 0 ? -x : x; +} + +/* In this source file, no f prototype in scope */ +long f(); + +long g(int x) { + return f(x); +} +``` + +## Compliant Solution + +In this compliant solution, the prototype for the function `f()` is included in the source file in the scope of where it is called, and the function `f()` is correctly called with an argument of type `long`: + +```cpp +/* In another source file */ + +long f(long x) { + return x < 0 ? -x : x; +} + +/* f prototype in scope in this source file */ + +long f(long x); + +long g(int x) { + return f((long)x); +} + +``` + +## Noncompliant Code Example (POSIX) + +The POSIX function `open()` \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\] is a variadic function with the following prototype: + +```cpp +int open(const char *path, int oflag, ... ); + +``` +The `open()` function accepts a third argument to determine a newly created file's access mode. If `open()` is used to create a new file and the third argument is omitted, the file may be created with unintended access permissions. (See [FIO06-C. Create files with appropriate access permissions](https://wiki.sei.cmu.edu/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions).) + +In this noncompliant code example from a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in the `useradd()` function of the `shadow-utils` package [CVE-2006-1174](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-1174), the third argument to `open()` is accidentally omitted: + +```cpp +fd = open(ms, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC); + +``` +Technically, it is incorrect to pass a third argument to open() when not creating a new file (that is, with the O_CREAT flag not set). + +## Compliant Solution (POSIX) + +In this compliant solution, a third argument is specified in the call to `open()`: + +```cpp +#include + +void func(const char *ms, mode_t perms) { + /* ... */ + int fd; + fd = open(ms, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC, perms); + if (fd == -1) { + /* Handle error */ + } +} +``` + +## Risk Assessment + +Calling a function with incorrect arguments can result in [unexpected](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior) or unintended program behavior. + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP37-C Medium Probable High P4 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 incompatible-argument-type parameter-match parameter-match-computed parameter-match-type Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-EXP37
CodeSonar 7.0p0 LANG.FUNCS.APM Array parameter mismatch
Compass/ROSE Can detect some violations of this rule. In particular, it ensures that all calls to open() supply exactly two arguments if the second argument does not involve O_CREAT , and exactly three arguments if the second argument does involve O_CREAT
Coverity 2017.07 MISRA C 2012 Rule 8.2 MISRA C 2012 Rule 17.3 Implemented Relies on functions declared with prototypes, allow compiler to check
ECLAIR 1.2 CC2.EXP37 Partially implemented
EDG
GCC 4.3.5 Can detect violation of this rule when the -Wstrict-prototypes flag is used. However, it cannot detect violations involving variadic functions, such as the open() example described earlier
Helix QAC 2022.2 C1331, C1332, C1333, C3002, C3320, C3335 C++0403
Klocwork 2022.2 MISRA.FUNC.UNMATCHED.PARAMS
LDRA tool suite 9.7.1 41 D, 21 S, 98 S, 170 S, 496 S, 576 S Partially implemented
Parasoft C/C++test 2022.1 CERT_C-EXP37-a CERT_C-EXP37-b CERT_C-EXP37-c CERT_C-EXP37-d Identifiers shall be given for all of the parameters in a function prototype declaration Function types shall have named parameters Function types shall be in prototype form Functions shall always have visible prototype at the function call
Polyspace Bug Finder R2022a CERT C: Rule EXP37-C Checks for: Implicit function declarationmplicit function declaration, bad file access mode or statusad file access mode or status, unreliable cast of function pointernreliable cast of function pointer, standard function call with incorrect argumentstandard function call with incorrect arguments. Rule partially covered.
PRQA QA-C 9.7 1331, 1332, 1333, 3002, 3320, 3335 Partially implemented
PRQA QA-C++ 4.4 0403
PVS-Studio 7.20 V540 , V541 , V549 , V575 , V632 , V639 , V666 , V671 , V742 , V743 , V764 , V1004
SonarQube C/C++ Plugin 3.11 S930 Detects incorrect argument count
RuleChecker 22.04 parameter-match parameter-match-type Partially checked
TrustInSoft Analyzer 1.38 unclassified ("function type matches") Partially verified (see one compliant and one 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+EXP37-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 Secure Coding Standard DCL07-C. Include the appropriate type information in function declarators Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard MSC00-C. Compile cleanly at high warning levels Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard FIO06-C. Create files with appropriate access permissions Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Subprogram Signature Mismatch \[OTR\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Calling functions with incorrect arguments \[argcomp\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 8.2 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 17.3 (mandatory) Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-628 , Function Call with Incorrectly Specified Arguments 2017-07-05: CERT: Rule subset of CWE
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-685 and EXP37-C** + +EXP37-C = Union( CWE-685, CWE-686) Intersection( CWE-685, CWE-686) = Ø + +**CWE-686 and EXP37-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 +EXP37-C = Union( CWE-685, CWE-686) + +Intersection( CWE-685, CWE-686) = Ø + +**CWE-628 and EXP37-C** + +CWE-628 = Union( EXP37-C, list) where list = + +* Improper ordering of function arguments (that does not violate argument types) +* Wrong argument values or references + +## Bibliography + +
\[ CVE \] CVE-2006-1174
\[ ISO/IEC 9899:2011 \] 6.3.2.3, "Pointers" 6.5.2.2, "Function Calls"
\[ IEEE Std 1003.1:2013 \] open()
\[ Spinellis 2006 \] Section 2.6.1, "Incorrect Routine or Arguments"
+ + +## Implementation notes + +The analysis of invalid parameter count passed to POSIX open calls only applies when the value of the flags argument is computed locally. + +## References + +* CERT-C: [EXP37-C: Call functions with the correct number and type of arguments](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql b/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql new file mode 100644 index 0000000000..275e4759d7 --- /dev/null +++ b/c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql @@ -0,0 +1,70 @@ +/** + * @id c/cert/call-posix-open-with-correct-argument-count + * @name EXP37-C: Pass the correct number of arguments to the POSIX open function. + * @description A third argument should be passed to the POSIX function open() when and only when + * creating a new file. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/exp37-c + * correctness + * security + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import semmle.code.cpp.commons.unix.Constants +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +int o_creat_val() { + result = any(MacroInvocation ma | ma.getMacroName() = "O_CREAT" | ma.getExpr().getValue().toInt()) + or + result = o_creat() +} + +/** + * A function call to POSIX open() + */ +class POSIXOpenFunctionCall extends FunctionCall { + POSIXOpenFunctionCall() { this.getTarget().getQualifiedName() = "open" } + + /** + * Holds if reasonable bounds exist for the value of the 'flags' argument of the call. + * This predicate will never hold for cases such as wrapper functions + * which pass a parameter to the open() 'flags' argument. + */ + predicate hasFlagsArgBounds() { lowerBound(this.getArgument(1)) >= 0 } + + /** + * Holds if the 'flags' argument contains the O_CREAT flag. + * Because this predicate uses the SimpleRangeAnalysis library, it only + * analyzes the bounds of 'flag' arguments which can be deduced locally. + */ + predicate isOpenCreateCall() { + hasFlagsArgBounds() and + upperBound(this.getArgument(1)).(int).bitAnd(o_creat_val()) != 0 + } +} + +from POSIXOpenFunctionCall call, string message +where + not isExcluded(call, ExpressionsPackage::callPOSIXOpenWithCorrectArgumentCountQuery()) and + // include only analyzable flag arguments which have values that can be determined locally + call.hasFlagsArgBounds() and + // differentiate between two variants: + // 1) a call to open() with the O_CREAT flag set, which should pass three arguments + // 2) a call to open without the O_CREAT flag set, which should pass two arguments + if call.isOpenCreateCall() + then ( + call.getNumberOfArguments() != 3 and + message = + "Call to " + call.getTarget().getName() + + " with O_CREAT flag does not pass exactly three arguments." + ) else ( + call.getNumberOfArguments() != 2 and + message = + "Call to " + call.getTarget().getName() + + " without O_CREAT flag does not pass exactly two arguments." + ) +select call, message diff --git a/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.md b/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.md new file mode 100644 index 0000000000..6fe7a74bcc --- /dev/null +++ b/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.md @@ -0,0 +1,243 @@ +# EXP37-C: Do not call a function pointer which points to a function of an incompatible type + +This query implements the CERT-C rule EXP37-C: + +> Call functions with the correct number and type of arguments + + +## Description + +Do not call a function with the wrong number or type of arguments. + +The C Standard identifies five distinct situations in which [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) (UB) may arise as a result of invoking a function using a declaration that is incompatible with its definition or by supplying incorrect types or numbers of arguments: + +
UB Description
26 A pointer is used to call a function whose type is not compatible with the referenced type (6.3.2.3).
38 For a call to a function without a function prototype in scope, the number of arguments does not equal the number of parameters (6.5.2.2).
39 For a call to a function without a function prototype in scope where the function is defined with a function prototype, either the prototype ends with an ellipsis or the types of the arguments after promotion are not compatible with the types of the parameters (6.5.2.2).
40 For a call to a function without a function prototype in scope where the function is not defined with a function prototype, the types of the arguments after promotion are not compatible with those of the parameters after promotion (with certain exceptions) (6.5.2.2).
41 A function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function (6.5.2.2).
+Functions that are appropriately declared (as in [DCL40-C. Do not create incompatible declarations of the same function or object](https://wiki.sei.cmu.edu/confluence/display/c/DCL40-C.+Do+not+create+incompatible+declarations+of+the+same+function+or+object)) will typically generate a compiler diagnostic message if they are supplied with the wrong number or types of arguments. However, there are cases in which supplying the incorrect arguments to a function will, at best, generate compiler [warnings](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-warning). Although such warnings should be resolved, they do not prevent program compilation. (See [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels).) + + +## Noncompliant Code Example + +The header `` provides type-generic macros for math functions. Although most functions from the `` header have a complex counterpart in ``, several functions do not. Calling any of the following type-generic functions with complex values is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +**Functions That Should Not Be Called with Complex Values** + +
atan2() erf() fdim() fmin() ilogb() llround() logb() nextafter() rint() tgamma()
cbrt() erfc() floor() fmod() ldexp() log10() lrint() nexttoward() round() trunc()
ceil() exp2() fma() frexp() lgamma() log1p() lround() remainder() scalbn()
copysign() expm1() fmax() hypot() llrint() log2() nearbyint() remquo() scalbln()
+This noncompliant code example attempts to take the base-2 logarithm of a complex number, resulting in undefined behavior: + + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log2(c); +} +``` + +## Compliant Solution (Complex Number) + +If the `clog2()` function is not available for an implementation as an extension, the programmer can take the base-2 logarithm of a complex number, using `log()` instead of `log2()`, because `log()` can be used on complex arguments, as shown in this compliant solution: + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log(c)/log(2); +} +``` + +## Compliant Solution (Real Number) + +The programmer can use this compliant solution if the intent is to take the base-2 logarithm of the real part of the complex number: + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log2(creal(c)); +} +``` + +## Noncompliant Code Example + +In this noncompliant example, the C standard library function `strchr()` is called through the function pointer `fp` declared with a prototype with incorrectly typed arguments. According to the C Standard, 6.3.2.3, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] + +> A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined. + + +See [undefined behavior 26.](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_26) + +```cpp +#include +#include + +char *(*fp)(); + +int main(void) { + const char *c; + fp = strchr; + c = fp('e', "Hello"); + printf("%s\n", c); + return 0; +} +``` + +## Compliant Solution + +In this compliant solution, the function pointer `fp`, which points to the C standard library function `strchr()`, is declared with the correct parameters and is invoked with the correct number and type of arguments: + +```cpp +#include +#include + +char *(*fp)(const char *, int); + +int main(void) { + const char *c; + fp = strchr; + c = fp("Hello",'e'); + printf("%s\n", c); + return 0; +} + +``` + +## Noncompliant Code Example + +In this noncompliant example, the function `f()` is defined to take an argument of type `long` but `f()` is called from another file with an argument of type `int`: + +```cpp +/* In another source file */ +long f(long x) { + return x < 0 ? -x : x; +} + +/* In this source file, no f prototype in scope */ +long f(); + +long g(int x) { + return f(x); +} +``` + +## Compliant Solution + +In this compliant solution, the prototype for the function `f()` is included in the source file in the scope of where it is called, and the function `f()` is correctly called with an argument of type `long`: + +```cpp +/* In another source file */ + +long f(long x) { + return x < 0 ? -x : x; +} + +/* f prototype in scope in this source file */ + +long f(long x); + +long g(int x) { + return f((long)x); +} + +``` + +## Noncompliant Code Example (POSIX) + +The POSIX function `open()` \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\] is a variadic function with the following prototype: + +```cpp +int open(const char *path, int oflag, ... ); + +``` +The `open()` function accepts a third argument to determine a newly created file's access mode. If `open()` is used to create a new file and the third argument is omitted, the file may be created with unintended access permissions. (See [FIO06-C. Create files with appropriate access permissions](https://wiki.sei.cmu.edu/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions).) + +In this noncompliant code example from a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in the `useradd()` function of the `shadow-utils` package [CVE-2006-1174](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-1174), the third argument to `open()` is accidentally omitted: + +```cpp +fd = open(ms, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC); + +``` +Technically, it is incorrect to pass a third argument to open() when not creating a new file (that is, with the O_CREAT flag not set). + +## Compliant Solution (POSIX) + +In this compliant solution, a third argument is specified in the call to `open()`: + +```cpp +#include + +void func(const char *ms, mode_t perms) { + /* ... */ + int fd; + fd = open(ms, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC, perms); + if (fd == -1) { + /* Handle error */ + } +} +``` + +## Risk Assessment + +Calling a function with incorrect arguments can result in [unexpected](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior) or unintended program behavior. + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP37-C Medium Probable High P4 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 incompatible-argument-type parameter-match parameter-match-computed parameter-match-type Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-EXP37
CodeSonar 7.0p0 LANG.FUNCS.APM Array parameter mismatch
Compass/ROSE Can detect some violations of this rule. In particular, it ensures that all calls to open() supply exactly two arguments if the second argument does not involve O_CREAT , and exactly three arguments if the second argument does involve O_CREAT
Coverity 2017.07 MISRA C 2012 Rule 8.2 MISRA C 2012 Rule 17.3 Implemented Relies on functions declared with prototypes, allow compiler to check
ECLAIR 1.2 CC2.EXP37 Partially implemented
EDG
GCC 4.3.5 Can detect violation of this rule when the -Wstrict-prototypes flag is used. However, it cannot detect violations involving variadic functions, such as the open() example described earlier
Helix QAC 2022.2 C1331, C1332, C1333, C3002, C3320, C3335 C++0403
Klocwork 2022.2 MISRA.FUNC.UNMATCHED.PARAMS
LDRA tool suite 9.7.1 41 D, 21 S, 98 S, 170 S, 496 S, 576 S Partially implemented
Parasoft C/C++test 2022.1 CERT_C-EXP37-a CERT_C-EXP37-b CERT_C-EXP37-c CERT_C-EXP37-d Identifiers shall be given for all of the parameters in a function prototype declaration Function types shall have named parameters Function types shall be in prototype form Functions shall always have visible prototype at the function call
Polyspace Bug Finder R2022a CERT C: Rule EXP37-C Checks for: Implicit function declarationmplicit function declaration, bad file access mode or statusad file access mode or status, unreliable cast of function pointernreliable cast of function pointer, standard function call with incorrect argumentstandard function call with incorrect arguments. Rule partially covered.
PRQA QA-C 9.7 1331, 1332, 1333, 3002, 3320, 3335 Partially implemented
PRQA QA-C++ 4.4 0403
PVS-Studio 7.20 V540 , V541 , V549 , V575 , V632 , V639 , V666 , V671 , V742 , V743 , V764 , V1004
SonarQube C/C++ Plugin 3.11 S930 Detects incorrect argument count
RuleChecker 22.04 parameter-match parameter-match-type Partially checked
TrustInSoft Analyzer 1.38 unclassified ("function type matches") Partially verified (see one compliant and one 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+EXP37-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 Secure Coding Standard DCL07-C. Include the appropriate type information in function declarators Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard MSC00-C. Compile cleanly at high warning levels Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard FIO06-C. Create files with appropriate access permissions Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Subprogram Signature Mismatch \[OTR\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Calling functions with incorrect arguments \[argcomp\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 8.2 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 17.3 (mandatory) Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-628 , Function Call with Incorrectly Specified Arguments 2017-07-05: CERT: Rule subset of CWE
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-685 and EXP37-C** + +EXP37-C = Union( CWE-685, CWE-686) Intersection( CWE-685, CWE-686) = Ø + +**CWE-686 and EXP37-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 +EXP37-C = Union( CWE-685, CWE-686) + +Intersection( CWE-685, CWE-686) = Ø + +**CWE-628 and EXP37-C** + +CWE-628 = Union( EXP37-C, list) where list = + +* Improper ordering of function arguments (that does not violate argument types) +* Wrong argument values or references + +## Bibliography + +
\[ CVE \] CVE-2006-1174
\[ ISO/IEC 9899:2011 \] 6.3.2.3, "Pointers" 6.5.2.2, "Function Calls"
\[ IEEE Std 1003.1:2013 \] open()
\[ Spinellis 2006 \] Section 2.6.1, "Incorrect Routine or Arguments"
+ + +## Implementation notes + +This query raises a result for a function assigned to a function pointer of an incompatible type even if the function pointer is never eventually called. + +## References + +* CERT-C: [EXP37-C: Call functions with the correct number and type of arguments](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql b/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql new file mode 100644 index 0000000000..17e1c0e0e9 --- /dev/null +++ b/c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql @@ -0,0 +1,62 @@ +/** + * @id c/cert/do-not-call-function-pointer-with-incompatible-type + * @name EXP37-C: Do not call a function pointer which points to a function of an incompatible type + * @description Calling a function pointer with a type incompatible with the function it points to + * is undefined. + * @kind path-problem + * @precision high + * @problem.severity error + * @tags external/cert/id/exp37-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import semmle.code.cpp.dataflow.DataFlow +import DataFlow::PathGraph + +/** + * An expression of type `FunctionPointer` which is the unconverted expression of a cast + * which converts the function pointer to a pointer to a function of a different type. + */ +class SuspiciousFunctionPointerCastExpr extends Expr { + SuspiciousFunctionPointerCastExpr() { + exists(CStyleCast cast, Type old, Type new | + this = cast.getUnconverted() and + old = cast.getUnconverted().getUnderlyingType() and + new = cast.getFullyConverted().getUnderlyingType() and + old != new and + old instanceof FunctionPointerType and + new instanceof FunctionPointerType + ) + } +} + +/** + * Data-flow configuration for flow from a `SuspiciousFunctionPointerCastExpr` + * to a call of the function pointer resulting from the function pointer cast + */ +class SuspectFunctionPointerToCallConfig extends DataFlow::Configuration { + SuspectFunctionPointerToCallConfig() { this = "SuspectFunctionPointerToCallConfig" } + + override predicate isSource(DataFlow::Node src) { + src.asExpr() instanceof SuspiciousFunctionPointerCastExpr + } + + override predicate isSink(DataFlow::Node sink) { + exists(VariableCall call | sink.asExpr() = call.getExpr().(VariableAccess)) + } +} + +from + SuspectFunctionPointerToCallConfig config, DataFlow::PathNode src, DataFlow::PathNode sink, + Access access +where + not isExcluded(src.getNode().asExpr(), + ExpressionsPackage::doNotCallFunctionPointerWithIncompatibleTypeQuery()) and + access = src.getNode().asExpr() and + config.hasFlowPath(src, sink) +select src, src, sink, + "Incompatible function $@ assigned to function pointer is eventually called through the pointer.", + access.getTarget(), access.getTarget().getName() diff --git a/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.md b/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.md new file mode 100644 index 0000000000..337095447e --- /dev/null +++ b/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.md @@ -0,0 +1,243 @@ +# EXP37-C: Do not pass arguments with an incompatible count or type to a function. + +This query implements the CERT-C rule EXP37-C: + +> Call functions with the correct number and type of arguments + + +## Description + +Do not call a function with the wrong number or type of arguments. + +The C Standard identifies five distinct situations in which [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) (UB) may arise as a result of invoking a function using a declaration that is incompatible with its definition or by supplying incorrect types or numbers of arguments: + +
UB Description
26 A pointer is used to call a function whose type is not compatible with the referenced type (6.3.2.3).
38 For a call to a function without a function prototype in scope, the number of arguments does not equal the number of parameters (6.5.2.2).
39 For a call to a function without a function prototype in scope where the function is defined with a function prototype, either the prototype ends with an ellipsis or the types of the arguments after promotion are not compatible with the types of the parameters (6.5.2.2).
40 For a call to a function without a function prototype in scope where the function is not defined with a function prototype, the types of the arguments after promotion are not compatible with those of the parameters after promotion (with certain exceptions) (6.5.2.2).
41 A function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function (6.5.2.2).
+Functions that are appropriately declared (as in [DCL40-C. Do not create incompatible declarations of the same function or object](https://wiki.sei.cmu.edu/confluence/display/c/DCL40-C.+Do+not+create+incompatible+declarations+of+the+same+function+or+object)) will typically generate a compiler diagnostic message if they are supplied with the wrong number or types of arguments. However, there are cases in which supplying the incorrect arguments to a function will, at best, generate compiler [warnings](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-warning). Although such warnings should be resolved, they do not prevent program compilation. (See [MSC00-C. Compile cleanly at high warning levels](https://wiki.sei.cmu.edu/confluence/display/c/MSC00-C.+Compile+cleanly+at+high+warning+levels).) + + +## Noncompliant Code Example + +The header `` provides type-generic macros for math functions. Although most functions from the `` header have a complex counterpart in ``, several functions do not. Calling any of the following type-generic functions with complex values is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). + +**Functions That Should Not Be Called with Complex Values** + +
atan2() erf() fdim() fmin() ilogb() llround() logb() nextafter() rint() tgamma()
cbrt() erfc() floor() fmod() ldexp() log10() lrint() nexttoward() round() trunc()
ceil() exp2() fma() frexp() lgamma() log1p() lround() remainder() scalbn()
copysign() expm1() fmax() hypot() llrint() log2() nearbyint() remquo() scalbln()
+This noncompliant code example attempts to take the base-2 logarithm of a complex number, resulting in undefined behavior: + + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log2(c); +} +``` + +## Compliant Solution (Complex Number) + +If the `clog2()` function is not available for an implementation as an extension, the programmer can take the base-2 logarithm of a complex number, using `log()` instead of `log2()`, because `log()` can be used on complex arguments, as shown in this compliant solution: + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log(c)/log(2); +} +``` + +## Compliant Solution (Real Number) + +The programmer can use this compliant solution if the intent is to take the base-2 logarithm of the real part of the complex number: + +```cpp +#include + +void func(void) { + double complex c = 2.0 + 4.0 * I; + double complex result = log2(creal(c)); +} +``` + +## Noncompliant Code Example + +In this noncompliant example, the C standard library function `strchr()` is called through the function pointer `fp` declared with a prototype with incorrectly typed arguments. According to the C Standard, 6.3.2.3, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] + +> A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined. + + +See [undefined behavior 26.](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_26) + +```cpp +#include +#include + +char *(*fp)(); + +int main(void) { + const char *c; + fp = strchr; + c = fp('e', "Hello"); + printf("%s\n", c); + return 0; +} +``` + +## Compliant Solution + +In this compliant solution, the function pointer `fp`, which points to the C standard library function `strchr()`, is declared with the correct parameters and is invoked with the correct number and type of arguments: + +```cpp +#include +#include + +char *(*fp)(const char *, int); + +int main(void) { + const char *c; + fp = strchr; + c = fp("Hello",'e'); + printf("%s\n", c); + return 0; +} + +``` + +## Noncompliant Code Example + +In this noncompliant example, the function `f()` is defined to take an argument of type `long` but `f()` is called from another file with an argument of type `int`: + +```cpp +/* In another source file */ +long f(long x) { + return x < 0 ? -x : x; +} + +/* In this source file, no f prototype in scope */ +long f(); + +long g(int x) { + return f(x); +} +``` + +## Compliant Solution + +In this compliant solution, the prototype for the function `f()` is included in the source file in the scope of where it is called, and the function `f()` is correctly called with an argument of type `long`: + +```cpp +/* In another source file */ + +long f(long x) { + return x < 0 ? -x : x; +} + +/* f prototype in scope in this source file */ + +long f(long x); + +long g(int x) { + return f((long)x); +} + +``` + +## Noncompliant Code Example (POSIX) + +The POSIX function `open()` \[[IEEE Std 1003.1:2013](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-IEEEStd1003.1-2013)\] is a variadic function with the following prototype: + +```cpp +int open(const char *path, int oflag, ... ); + +``` +The `open()` function accepts a third argument to determine a newly created file's access mode. If `open()` is used to create a new file and the third argument is omitted, the file may be created with unintended access permissions. (See [FIO06-C. Create files with appropriate access permissions](https://wiki.sei.cmu.edu/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions).) + +In this noncompliant code example from a [vulnerability](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) in the `useradd()` function of the `shadow-utils` package [CVE-2006-1174](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-1174), the third argument to `open()` is accidentally omitted: + +```cpp +fd = open(ms, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC); + +``` +Technically, it is incorrect to pass a third argument to open() when not creating a new file (that is, with the O_CREAT flag not set). + +## Compliant Solution (POSIX) + +In this compliant solution, a third argument is specified in the call to `open()`: + +```cpp +#include + +void func(const char *ms, mode_t perms) { + /* ... */ + int fd; + fd = open(ms, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC, perms); + if (fd == -1) { + /* Handle error */ + } +} +``` + +## Risk Assessment + +Calling a function with incorrect arguments can result in [unexpected](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior) or unintended program behavior. + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP37-C Medium Probable High P4 L3
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 incompatible-argument-type parameter-match parameter-match-computed parameter-match-type Fully checked
Axivion Bauhaus Suite 7.2.0 CertC-EXP37
CodeSonar 7.0p0 LANG.FUNCS.APM Array parameter mismatch
Compass/ROSE Can detect some violations of this rule. In particular, it ensures that all calls to open() supply exactly two arguments if the second argument does not involve O_CREAT , and exactly three arguments if the second argument does involve O_CREAT
Coverity 2017.07 MISRA C 2012 Rule 8.2 MISRA C 2012 Rule 17.3 Implemented Relies on functions declared with prototypes, allow compiler to check
ECLAIR 1.2 CC2.EXP37 Partially implemented
EDG
GCC 4.3.5 Can detect violation of this rule when the -Wstrict-prototypes flag is used. However, it cannot detect violations involving variadic functions, such as the open() example described earlier
Helix QAC 2022.2 C1331, C1332, C1333, C3002, C3320, C3335 C++0403
Klocwork 2022.2 MISRA.FUNC.UNMATCHED.PARAMS
LDRA tool suite 9.7.1 41 D, 21 S, 98 S, 170 S, 496 S, 576 S Partially implemented
Parasoft C/C++test 2022.1 CERT_C-EXP37-a CERT_C-EXP37-b CERT_C-EXP37-c CERT_C-EXP37-d Identifiers shall be given for all of the parameters in a function prototype declaration Function types shall have named parameters Function types shall be in prototype form Functions shall always have visible prototype at the function call
Polyspace Bug Finder R2022a CERT C: Rule EXP37-C Checks for: Implicit function declarationmplicit function declaration, bad file access mode or statusad file access mode or status, unreliable cast of function pointernreliable cast of function pointer, standard function call with incorrect argumentstandard function call with incorrect arguments. Rule partially covered.
PRQA QA-C 9.7 1331, 1332, 1333, 3002, 3320, 3335 Partially implemented
PRQA QA-C++ 4.4 0403
PVS-Studio 7.20 V540 , V541 , V549 , V575 , V632 , V639 , V666 , V671 , V742 , V743 , V764 , V1004
SonarQube C/C++ Plugin 3.11 S930 Detects incorrect argument count
RuleChecker 22.04 parameter-match parameter-match-type Partially checked
TrustInSoft Analyzer 1.38 unclassified ("function type matches") Partially verified (see one compliant and one 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+EXP37-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 Secure Coding Standard DCL07-C. Include the appropriate type information in function declarators Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard MSC00-C. Compile cleanly at high warning levels Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard FIO06-C. Create files with appropriate access permissions Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TR 24772:2013 Subprogram Signature Mismatch \[OTR\] Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Calling functions with incorrect arguments \[argcomp\] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 8.2 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 17.3 (mandatory) Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-628 , Function Call with Incorrectly Specified Arguments 2017-07-05: CERT: Rule subset of CWE
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-685 and EXP37-C** + +EXP37-C = Union( CWE-685, CWE-686) Intersection( CWE-685, CWE-686) = Ø + +**CWE-686 and EXP37-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 +EXP37-C = Union( CWE-685, CWE-686) + +Intersection( CWE-685, CWE-686) = Ø + +**CWE-628 and EXP37-C** + +CWE-628 = Union( EXP37-C, list) where list = + +* Improper ordering of function arguments (that does not violate argument types) +* Wrong argument values or references + +## Bibliography + +
\[ CVE \] CVE-2006-1174
\[ ISO/IEC 9899:2011 \] 6.3.2.3, "Pointers" 6.5.2.2, "Function Calls"
\[ IEEE Std 1003.1:2013 \] open()
\[ Spinellis 2006 \] Section 2.6.1, "Incorrect Routine or Arguments"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [EXP37-C: Call functions with the correct number and type of arguments](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql b/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql new file mode 100644 index 0000000000..8285f1d36a --- /dev/null +++ b/c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql @@ -0,0 +1,28 @@ +/** + * @id c/cert/do-not-call-functions-with-incompatible-arguments + * @name EXP37-C: Do not pass arguments with an incompatible count or type to a function. + * @description The arguments passed to a function must be compatible with the function's + * parameters. + * @kind problem + * @precision high + * @problem.severity error + * @tags external/cert/id/exp37-c + * correctness + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert +import codingstandards.cpp.MistypedFunctionArguments + +from FunctionCall fc, Function f, Parameter p +where + not isExcluded(fc, ExpressionsPackage::doNotCallFunctionsWithIncompatibleArgumentsQuery()) and + ( + mistypedFunctionArguments(fc, f, p) + or + complexArgumentPassedToRealParameter(fc, f, p) + ) +select fc, + "Argument $@ in call to " + f.toString() + " is incompatible with parameter " + p.getTypedName() + + ".", fc.getArgument(p.getIndex()) as arg, arg.toString() diff --git a/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.md b/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.md new file mode 100644 index 0000000000..64b5db9c4e --- /dev/null +++ b/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.md @@ -0,0 +1,74 @@ +# EXP46-C: Do not use a bitwise operator with a Boolean-like operand + +This query implements the CERT-C rule EXP46-C: + +> Do not use a bitwise operator with a Boolean-like operand + + +## Description + +Mixing bitwise and relational operators in the same full expression can be a sign of a logic error in the expression where a logical operator is usually the intended operator. Do not use the bitwise AND (`&`), bitwise OR (`|`), or bitwise XOR (`^`) operators with an operand of type `_Bool`, or the result of a *relational-expression* or *equality-expression*. If the bitwise operator is intended, it should be indicated with use of a parenthesized expression. + +## Noncompliant Code Example + +In this noncompliant code example, a bitwise `&` operator is used with the results of an *equality-expression*: + +```cpp +if (!(getuid() & geteuid() == 0)) { + /* ... */ +} + +``` + +## Compliant Solution + +This compliant solution uses the `&&` operator for the logical operation within the conditional expression: + +```cpp +if (!(getuid() && geteuid() == 0)) { + /* ... */ +} + +``` + +## Risk Assessment + +
Rule Severity Likelihood Remediation Cost Priority Level
EXP46-C Low Likely Low P9 L2
+ + +## Automated Detection + +
Tool Version Checker Description
Astrée 22.04 inappropriate-bool Supported indirectly via MISRA C:2012 Rule 10.1
Axivion Bauhaus Suite 7.2.0 CertC-EXP46
CodeSonar 7.0p0 LANG.TYPE.IOT Inappropriate operand type
Coverity 2017.07 CONSTANT_EXPRESSION_RESULT Partially implemented
Cppcheck 1.66 cert.py Detected by the addon cert.py
Helix QAC 2022.2 C3344, C4502 C++3709
Klocwork 2022.2 MISRA.LOGIC.OPERATOR.NOT_BOOL
LDRA tool suite 9.7.1 136 S Fully Implemented
Parasoft C/C++test 2022.1 CERT_C-EXP46-b Expressions that are effectively Boolean should not be used as operands to operators other than (&&, ||, !, =, ==, !=, ?:)
PC-lint Plus 1.4 514 Fully supported
Polyspace Bug Finder R2022a CERT C: Rule EXP46-C Checks for bitwise operations on boolean operands (rule fully covered)
PRQA QA-C 9.7 3344,4502
PRQA QA-C++ 4.4 3709
PVS-Studio 7.20 V564, V1015
RuleChecker 22.04 inappropriate-bool Supported indirectly via MISRA C:2012 Rule 10.1
+ + +## Related Guidelines + +[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions) + +
Taxonomy Taxonomy item Relationship
ISO/IEC TR 24772:2013 Likely Incorrect Expression \[KOA\] Prior to 2018-01-12: CERT: Unspecified Relationship
CWE 2.11 CWE-480 , Use of incorrect operator 2017-07-05: CERT: Rule subset of CWE
CWE 2.11 CWE-569 2017-07-06: CERT: Rule subset of CWE
+ + +## CERT-CWE Mapping Notes + +[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes + +**CWE-480 and EXP46-C** + +Intersection( EXP45-C, EXP46-C) = Ø + +CWE-480 = Union( EXP46-C, list) where list = + +* Usage of incorrect operator besides s/&/&&/ or s/|/||/ + +## Bibliography + +
\[ Hatton 1995 \] Section 2.7.2, "Errors of Omission and Addition"
+ + +## Implementation notes + +None + +## References + +* CERT-C: [EXP46-C: Do not use a bitwise operator with a Boolean-like operand](https://wiki.sei.cmu.edu/confluence/display/c) diff --git a/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql b/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql new file mode 100644 index 0000000000..040a8bb6ee --- /dev/null +++ b/c/cert/src/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql @@ -0,0 +1,49 @@ +/** + * @id c/cert/do-not-use-a-bitwise-operator-with-a-boolean-like-operand + * @name EXP46-C: Do not use a bitwise operator with a Boolean-like operand + * @description Using bitwise operators with unparenthesized Boolean-like operands may indicate a + * logic error. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/cert/id/exp46-c + * maintainability + * readability + * external/cert/obligation/rule + */ + +import cpp +import codingstandards.c.cert + +/** + * Holds if `op` is a bitwise AND, OR, or XOR expression + */ +predicate isBitwiseOperationPotentiallyAmbiguous(BinaryBitwiseOperation op) { + op instanceof BitwiseAndExpr or + op instanceof BitwiseOrExpr or + op instanceof BitwiseXorExpr +} + +/** + * Holds if `e` is an unparenthesised boolean expression, + * relational operation, or equality operation. + */ +predicate isDisallowedBitwiseOperationOperand(Expr e) { + not e.isParenthesised() and + ( + e.getFullyConverted().getUnderlyingType() instanceof BoolType or + e instanceof RelationalOperation or + e instanceof EqualityOperation + ) +} + +from Expr operand, Operation operation +where + not isExcluded(operation, + ExpressionsPackage::doNotUseABitwiseOperatorWithABooleanLikeOperandQuery()) and + isBitwiseOperationPotentiallyAmbiguous(operation) and + operand = operation.getAnOperand() and + isDisallowedBitwiseOperationOperand(operand) +select operation, + "Bitwise operator " + operation.getOperator() + + " performs potentially unintended operation on $@.", operand, "boolean operand" diff --git a/c/cert/test/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.expected b/c/cert/test/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.expected new file mode 100644 index 0000000000..8c50bd28db --- /dev/null +++ b/c/cert/test/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.expected @@ -0,0 +1,6 @@ +| test.c:18:3:18:6 | call to open | Call to open without O_CREAT flag does not pass exactly two arguments. | +| test.c:19:3:19:6 | call to open | Call to open with O_CREAT flag does not pass exactly three arguments. | +| test.c:23:3:23:6 | call to open | Call to open without O_CREAT flag does not pass exactly two arguments. | +| test.c:26:3:26:6 | call to open | Call to open with O_CREAT flag does not pass exactly three arguments. | +| test.c:34:3:34:6 | call to open | Call to open without O_CREAT flag does not pass exactly two arguments. | +| test.c:35:3:35:6 | call to open | Call to open with O_CREAT flag does not pass exactly three arguments. | diff --git a/c/cert/test/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.qlref b/c/cert/test/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.qlref new file mode 100644 index 0000000000..4873e7e27a --- /dev/null +++ b/c/cert/test/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.qlref @@ -0,0 +1 @@ +rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.expected b/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.expected new file mode 100644 index 0000000000..4c18bb2672 --- /dev/null +++ b/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.expected @@ -0,0 +1,24 @@ +edges +| test.c:48:68:48:70 | fns [f1] | test.c:49:3:49:5 | fns [f1] | +| test.c:49:3:49:5 | fns [f1] | test.c:49:8:49:9 | f1 | +| test.c:61:28:61:29 | f2 | test.c:62:3:62:11 | v1_called | +| test.c:73:3:73:5 | fns [post update] [f1] | test.c:75:45:75:48 | & ... [f1] | +| test.c:73:3:73:13 | ... = ... | test.c:73:3:73:5 | fns [post update] [f1] | +| test.c:73:12:73:13 | v2 | test.c:73:3:73:13 | ... = ... | +| test.c:75:45:75:48 | & ... [f1] | test.c:48:68:48:70 | fns [f1] | +nodes +| test.c:48:68:48:70 | fns [f1] | semmle.label | fns [f1] | +| test.c:49:3:49:5 | fns [f1] | semmle.label | fns [f1] | +| test.c:49:8:49:9 | f1 | semmle.label | f1 | +| test.c:61:28:61:29 | f2 | semmle.label | f2 | +| test.c:62:3:62:11 | v1_called | semmle.label | v1_called | +| test.c:70:9:70:17 | v3_called | semmle.label | v3_called | +| test.c:73:3:73:5 | fns [post update] [f1] | semmle.label | fns [post update] [f1] | +| test.c:73:3:73:13 | ... = ... | semmle.label | ... = ... | +| test.c:73:12:73:13 | v2 | semmle.label | v2 | +| test.c:75:45:75:48 | & ... [f1] | semmle.label | & ... [f1] | +subpaths +#select +| test.c:61:28:61:29 | f2 | test.c:61:28:61:29 | f2 | test.c:62:3:62:11 | v1_called | Incompatible function $@ assigned to function pointer is eventually called through the pointer. | test.c:41:13:41:14 | f2 | f2 | +| test.c:70:9:70:17 | v3_called | test.c:70:9:70:17 | v3_called | test.c:70:9:70:17 | v3_called | Incompatible function $@ assigned to function pointer is eventually called through the pointer. | test.c:58:7:58:15 | v3_called | v3_called | +| test.c:73:12:73:13 | v2 | test.c:73:12:73:13 | v2 | test.c:49:8:49:9 | f1 | Incompatible function $@ assigned to function pointer is eventually called through the pointer. | test.c:56:7:56:8 | v2 | v2 | diff --git a/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.qlref b/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.qlref new file mode 100644 index 0000000000..3d35c584c2 --- /dev/null +++ b/c/cert/test/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.qlref @@ -0,0 +1 @@ +rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected new file mode 100644 index 0000000000..a2ff1cdd92 --- /dev/null +++ b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.expected @@ -0,0 +1,3 @@ +| test.c:83:12:83:16 | call to atan2 | Argument $@ in call to atan2 is incompatible with parameter double (unnamed parameter 0). | test.c:83:18:83:18 | c | c | +| test.c:93:3:93:12 | call to test_func1 | Argument $@ in call to test_func1 is incompatible with parameter short p1. | test.c:93:14:93:15 | p1 | p1 | +| test.c:94:3:94:12 | call to test_func1 | Argument $@ in call to test_func1 is incompatible with parameter short p1. | test.c:94:14:94:15 | p2 | p2 | diff --git a/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.qlref b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.qlref new file mode 100644 index 0000000000..a7423fdb1b --- /dev/null +++ b/c/cert/test/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.qlref @@ -0,0 +1 @@ +rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP37-C/test.c b/c/cert/test/rules/EXP37-C/test.c new file mode 100644 index 0000000000..c1026c39d1 --- /dev/null +++ b/c/cert/test/rules/EXP37-C/test.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +const int G_FLAG_TEST = O_RDONLY; +const int G_FLAG_CREATE_TEST = O_RDWR | O_APPEND | O_CREAT; + +void test_open_nested(const char *path, int flags) { + open(path, flags); // NON_COMPLIANT[FALSE_NEGATIVE] + open(path, flags, 0); // COMPLIANT +} + +void test_open(const char *path) { + int flags; + + open(path, O_APPEND | O_RDWR); // COMPLIANT + open(path, O_APPEND | O_RDWR, 0); // NON_COMPLIANT + open(path, O_CREAT); // NON_COMPLIANT + + flags = O_APPEND | O_RDWR; + open(path, flags); // COMPLIANT + open(path, flags, 0); // NON_COMPLIANT + + flags |= O_CREAT; + open(path, flags); // NON_COMPLIANT + open(path, flags, 0); // COMPLIANT + test_open_nested(path, flags); + + flags &= ~O_CREAT; + open(path, flags); // COMPLIANT + open(path, flags, 0); // NON_COMPLIANT[FALSE_NEGATIVE] + + open(path, G_FLAG_TEST, 0); // NON_COMPLIANT + open(path, G_FLAG_CREATE_TEST); // NON_COMPLIANT +} + +typedef void (*fp1)(void); +typedef void (*fp2)(int p1); +extern void f1(void); +extern void f2(int p1); + +struct f_ptr_table { + fp1 f1; + fp2 f2; +}; + +void test_incompatible_function_pointer_nested(struct f_ptr_table *fns) { + fns->f1(); + fns->f2(0); +} + +void test_incompatible_function_pointer(void) { + fp1 v1 = NULL; + fp1 v1_called = f2; // COMPLIANT + fp2 v2 = NULL; + fp2 v2_called = NULL; + fp2 v3_called = NULL; + + v1 = (fp1)(void *)f2; // COMPLIANT + v1_called = (fp1)(void *)f2; // NON_COMPLIANT + v1_called(); + + v2 = f2; // COMPLIANT + v2_called = f2; // COMPLIANT + v2_called(0); + + v3_called = v2; // COMPLIANT + v3_called(0); + ((fp1)v3_called)(); // NON_COMPLIANT + + struct f_ptr_table fns; + fns.f1 = v2; // NON_COMPLIANT + fns.f2 = v2; // COMPLIANT + test_incompatible_function_pointer_nested(&fns); +} + +void test_complex_types(void) { + double r = 1.0; + double complex c = r * I; + double complex result; + + result = atan2(c, r); // NON_COMPLIANT + result = atan2(creal(c), r); // COMPLIANT + result = casin(c); // COMPLIANT +} + +void test_func1(); +void test_func2(int); +void test_func3(int p1, ...); + +void test_incompatible_arguments(int p1, short p2) { + test_func1(p1); // NON_COMPLIANT + test_func1(p2); // NON_COMPLIANT + ((int (*)(short))test_func1)(p2); // COMPLIANT + test_func2(0); // COMPLIANT + test_func3(0); // NON_COMPLIANT[FALSE_NEGATIVE] + test_func3(0, 1); // NON_COMPLIANT[FALSE_NEGATIVE] +} \ No newline at end of file diff --git a/c/cert/test/rules/EXP37-C/test2.c b/c/cert/test/rules/EXP37-C/test2.c new file mode 100644 index 0000000000..a1c567d647 --- /dev/null +++ b/c/cert/test/rules/EXP37-C/test2.c @@ -0,0 +1,5 @@ +void test_func1(short p1) {} + +void test_func2(int p1) {} + +void test_func3(int p1) {} \ No newline at end of file diff --git a/c/cert/test/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.expected b/c/cert/test/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.expected new file mode 100644 index 0000000000..a8242d6c17 --- /dev/null +++ b/c/cert/test/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.expected @@ -0,0 +1,2 @@ +| test.c:4:12:4:21 | ... & ... | Bitwise operator & performs potentially unintended operation on $@. | test.c:4:16:4:21 | ... == ... | boolean operand | +| test.c:14:9:14:38 | ... & ... | Bitwise operator & performs potentially unintended operation on $@. | test.c:14:23:14:38 | ... == ... | boolean operand | diff --git a/c/cert/test/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.qlref b/c/cert/test/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.qlref new file mode 100644 index 0000000000..fe9e501f6f --- /dev/null +++ b/c/cert/test/rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.qlref @@ -0,0 +1 @@ +rules/EXP46-C/DoNotUseABitwiseOperatorWithABooleanLikeOperand.ql \ No newline at end of file diff --git a/c/cert/test/rules/EXP46-C/test.c b/c/cert/test/rules/EXP46-C/test.c new file mode 100644 index 0000000000..97558674e4 --- /dev/null +++ b/c/cert/test/rules/EXP46-C/test.c @@ -0,0 +1,20 @@ +extern int f_get_int(); + +void test() { + (void)(!(1 & 2 == 0)); // NON_COMPLIANT + (void)(!(1 && 2 == 0)); // COMPLIANT + (void)(!((1 & 2) == 0)); // COMPLIANT + + if (1 & 2) { // COMPLIANT + } + if (1 | 2) { // COMPLIANT + } + if (1 && 2) { // COMPLIANT + } + if (!(f_get_int() & f_get_int() == 0)) { // NON_COMPLIANT + } + if (!(f_get_int() & (f_get_int() == 0))) { // COMPLIANT + } + if (!(f_get_int() && f_get_int() == 0)) { // COMPLIANT + } +} \ No newline at end of file diff --git a/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql b/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql index 9825eae3f9..bfac04da6f 100644 --- a/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql +++ b/c/misra/src/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.ql @@ -25,11 +25,5 @@ where // exception 1 (null pointer constant) not isNullPointerConstant(cast.getExpr()) and // exception 2 (conversion to void) - not newType instanceof VoidType and - // exception 3 (implicit conversion of function to function pointer) - not ( - cast.isImplicit() and - newType instanceof FunctionPointerType and - cast.getExpr() instanceof FunctionAccess - ) + not newType instanceof VoidType select cast, "Cast performed between a function pointer and another type." diff --git a/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected b/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected index 05dace2c7d..ebe2c74742 100644 --- a/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected +++ b/c/misra/test/rules/RULE-11-1/ConversionBetweenFunctionPointerAndOtherType.expected @@ -3,3 +3,6 @@ | test.c:12:14:12:23 | (void *)... | Cast performed between a function pointer and another type. | | test.c:14:8:14:15 | (fp2)... | Cast performed between a function pointer and another type. | | test.c:15:8:15:15 | (fp2)... | Cast performed between a function pointer and another type. | +| test.c:22:12:22:13 | (fp1)... | Cast performed between a function pointer and another type. | +| test.c:25:8:25:9 | (fp1)... | Cast performed between a function pointer and another type. | +| test.c:27:8:27:25 | (..(*)(..))... | Cast performed between a function pointer and another type. | diff --git a/c/misra/test/rules/RULE-11-1/test.c b/c/misra/test/rules/RULE-11-1/test.c index 0c87fab719..858c6e68a9 100644 --- a/c/misra/test/rules/RULE-11-1/test.c +++ b/c/misra/test/rules/RULE-11-1/test.c @@ -18,6 +18,11 @@ void f1(void) { (void)(*v4()); // COMPLIANT extern void f2(int p1); - f2(0); // COMPLIANT - fp1 v5 = f2; // COMPLIANT + f2(0); // COMPLIANT + fp1 v5 = f2; // NON_COMPLIANT + fp2 v6 = f2; // COMPLIANT + v5 = v1; // COMPLIANT + v5 = v2; // NON_COMPLIANT + v5 = (void (*)(void))v1; // COMPLIANT + v5 = (void (*)(void))v2; // NON_COMPLIANT } \ No newline at end of file diff --git a/change_notes/2022-08-12-fix-RULE-11-1-false-negative.md b/change_notes/2022-08-12-fix-RULE-11-1-false-negative.md new file mode 100644 index 0000000000..032e1e07d3 --- /dev/null +++ b/change_notes/2022-08-12-fix-RULE-11-1-false-negative.md @@ -0,0 +1,3 @@ + - `RULE-11-1` - `ConversionBetweenFunctionPointerAndOtherType.ql`: + - A result is now reported for an implicit conversion of a pointer to a function into a pointer to a function with an incompatible type. + - Modified the test to reflect the fixed coverage. \ No newline at end of file diff --git a/cpp/common/src/codingstandards/cpp/MistypedFunctionArguments.qll b/cpp/common/src/codingstandards/cpp/MistypedFunctionArguments.qll new file mode 100644 index 0000000000..6fe90372da --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/MistypedFunctionArguments.qll @@ -0,0 +1,114 @@ +/** + * Provides the implementation of the MistypedFunctionArguments query. The + * query is implemented as a library, so that we can avoid producing + * duplicate results in other similar queries. + * + * The implementation of this library is based on the following file from the standard library: + * codeql/cpp/ql/src/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.qll + */ + +import cpp + +pragma[inline] +private predicate arithTypesMatch(Type arg, Type parm) { + arg = parm + or + arg.getSize() = parm.getSize() and + ( + arg instanceof IntegralOrEnumType and + parm instanceof IntegralOrEnumType + or + arg instanceof FloatingPointType and + parm instanceof FloatingPointType + ) +} + +pragma[inline] +private predicate nestedPointerArgTypeMayBeUsed(Type arg, Type parm) { + // arithmetic types + arithTypesMatch(arg, parm) + or + // conversion to/from pointers to void is allowed + arg instanceof VoidType + or + parm instanceof VoidType +} + +pragma[inline] +private predicate pointerArgTypeMayBeUsed(Type arg, Type parm) { + nestedPointerArgTypeMayBeUsed(arg, parm) + or + // nested pointers + nestedPointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(), + parm.(PointerType).getBaseType().getUnspecifiedType()) + or + nestedPointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(), + parm.(PointerType).getBaseType().getUnspecifiedType()) +} + +pragma[inline] +private predicate argTypeMayBeUsed(Type arg, Type parm) { + // arithmetic types + arithTypesMatch(arg, parm) + or + // pointers to compatible types + pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(), + parm.(PointerType).getBaseType().getUnspecifiedType()) + or + pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(), + parm.(PointerType).getBaseType().getUnspecifiedType()) + or + // C11 arrays + pointerArgTypeMayBeUsed(arg.(PointerType).getBaseType().getUnspecifiedType(), + parm.(ArrayType).getBaseType().getUnspecifiedType()) + or + pointerArgTypeMayBeUsed(arg.(ArrayType).getBaseType().getUnspecifiedType(), + parm.(ArrayType).getBaseType().getUnspecifiedType()) +} + +// This predicate holds whenever expression `arg` may be used to initialize +// function parameter `parm` without need for run-time conversion. +pragma[inline] +private predicate argMayBeUsed(Expr arg, Parameter parm) { + argTypeMayBeUsed(arg.getFullyConverted().getUnspecifiedType(), parm.getUnspecifiedType()) +} + +// True if function was ()-declared, but not (void)-declared or K&R-defined +private predicate hasZeroParamDecl(Function f) { + exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() | + not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition() + ) +} + +// True if this file (or header) was compiled as a C file +private predicate isCompiledAsC(File f) { + f.compiledAsC() + or + exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f) +} + +private predicate isTypeInComplexDomain(FloatingPointType type) { + type.getUnderlyingType().(FloatingPointType).getDomain() instanceof ComplexDomain +} + +predicate mistypedFunctionArguments(FunctionCall fc, Function f, Parameter p) { + f = fc.getTarget() and + p = f.getAParameter() and + hasZeroParamDecl(f) and + isCompiledAsC(f.getFile()) and + not f.isVarargs() and + not f instanceof BuiltInFunction and + p.getIndex() < fc.getNumberOfArguments() and + // Parameter p and its corresponding call argument must have mismatched types + not argMayBeUsed(fc.getArgument(p.getIndex()), p) +} + +predicate complexArgumentPassedToRealParameter(FunctionCall fc, Function f, Parameter p) { + f = fc.getTarget() and + p = f.getAParameter() and + // Some implementations implicitly convert complex floating point values by + // extracting the real part of the complex number (in-place or via a creal() call). + // This predicate holds in those cases unless the value is explicitly converted. + not isTypeInComplexDomain(p.getType()) and + isTypeInComplexDomain(fc.getArgument(p.getIndex()).getExplicitlyConverted().getType()) +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions.qll new file mode 100644 index 0000000000..2e18feca23 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/Expressions.qll @@ -0,0 +1,74 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype ExpressionsQuery = + TDoNotCallFunctionPointerWithIncompatibleTypeQuery() or + TDoNotCallFunctionsWithIncompatibleArgumentsQuery() or + TCallPOSIXOpenWithCorrectArgumentCountQuery() or + TDoNotUseABitwiseOperatorWithABooleanLikeOperandQuery() + +predicate isExpressionsQueryMetadata(Query query, string queryId, string ruleId) { + query = + // `Query` instance for the `doNotCallFunctionPointerWithIncompatibleType` query + ExpressionsPackage::doNotCallFunctionPointerWithIncompatibleTypeQuery() and + queryId = + // `@id` for the `doNotCallFunctionPointerWithIncompatibleType` query + "c/cert/do-not-call-function-pointer-with-incompatible-type" and + ruleId = "EXP37-C" + or + query = + // `Query` instance for the `doNotCallFunctionsWithIncompatibleArguments` query + ExpressionsPackage::doNotCallFunctionsWithIncompatibleArgumentsQuery() and + queryId = + // `@id` for the `doNotCallFunctionsWithIncompatibleArguments` query + "c/cert/do-not-call-functions-with-incompatible-arguments" and + ruleId = "EXP37-C" + or + query = + // `Query` instance for the `callPOSIXOpenWithCorrectArgumentCount` query + ExpressionsPackage::callPOSIXOpenWithCorrectArgumentCountQuery() and + queryId = + // `@id` for the `callPOSIXOpenWithCorrectArgumentCount` query + "c/cert/call-posix-open-with-correct-argument-count" and + ruleId = "EXP37-C" + or + query = + // `Query` instance for the `doNotUseABitwiseOperatorWithABooleanLikeOperand` query + ExpressionsPackage::doNotUseABitwiseOperatorWithABooleanLikeOperandQuery() and + queryId = + // `@id` for the `doNotUseABitwiseOperatorWithABooleanLikeOperand` query + "c/cert/do-not-use-a-bitwise-operator-with-a-boolean-like-operand" and + ruleId = "EXP46-C" +} + +module ExpressionsPackage { + Query doNotCallFunctionPointerWithIncompatibleTypeQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCallFunctionPointerWithIncompatibleType` query + TQueryC(TExpressionsPackageQuery(TDoNotCallFunctionPointerWithIncompatibleTypeQuery())) + } + + Query doNotCallFunctionsWithIncompatibleArgumentsQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotCallFunctionsWithIncompatibleArguments` query + TQueryC(TExpressionsPackageQuery(TDoNotCallFunctionsWithIncompatibleArgumentsQuery())) + } + + Query callPOSIXOpenWithCorrectArgumentCountQuery() { + //autogenerate `Query` type + result = + // `Query` type for `callPOSIXOpenWithCorrectArgumentCount` query + TQueryC(TExpressionsPackageQuery(TCallPOSIXOpenWithCorrectArgumentCountQuery())) + } + + Query doNotUseABitwiseOperatorWithABooleanLikeOperandQuery() { + //autogenerate `Query` type + result = + // `Query` type for `doNotUseABitwiseOperatorWithABooleanLikeOperand` query + TQueryC(TExpressionsPackageQuery(TDoNotUseABitwiseOperatorWithABooleanLikeOperandQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll index 6b6915ad3b..c67a514009 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll @@ -8,6 +8,7 @@ import Concurrency2 import Concurrency3 import Contracts1 import Declarations1 +import Expressions import IO1 import IO2 import IO3 @@ -33,6 +34,7 @@ newtype TCQuery = TConcurrency3PackageQuery(Concurrency3Query q) or TContracts1PackageQuery(Contracts1Query q) or TDeclarations1PackageQuery(Declarations1Query q) or + TExpressionsPackageQuery(ExpressionsQuery q) or TIO1PackageQuery(IO1Query q) or TIO2PackageQuery(IO2Query q) or TIO3PackageQuery(IO3Query q) or @@ -58,6 +60,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) { isConcurrency3QueryMetadata(query, queryId, ruleId) or isContracts1QueryMetadata(query, queryId, ruleId) or isDeclarations1QueryMetadata(query, queryId, ruleId) or + isExpressionsQueryMetadata(query, queryId, ruleId) or isIO1QueryMetadata(query, queryId, ruleId) or isIO2QueryMetadata(query, queryId, ruleId) or isIO3QueryMetadata(query, queryId, ruleId) or diff --git a/rule_packages/c/Expressions.json b/rule_packages/c/Expressions.json new file mode 100644 index 0000000000..6ed56610af --- /dev/null +++ b/rule_packages/c/Expressions.json @@ -0,0 +1,72 @@ +{ + "CERT-C": { + "EXP37-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Calling a function pointer with a type incompatible with the function it points to is undefined.", + "kind": "path-problem", + "name": "Do not call a function pointer which points to a function of an incompatible type", + "precision": "high", + "severity": "error", + "short_name": "DoNotCallFunctionPointerWithIncompatibleType", + "tags": [ + "correctness" + ], + "implementation_scope": { + "description": "This query raises a result for a function assigned to a function pointer of an incompatible type even if the function pointer is never eventually called." + } + }, + { + "description": "The arguments passed to a function must be compatible with the function's parameters.", + "kind": "problem", + "name": "Do not pass arguments with an incompatible count or type to a function.", + "precision": "high", + "severity": "error", + "short_name": "DoNotCallFunctionsWithIncompatibleArguments", + "tags": [ + "correctness" + ] + }, + { + "description": "A third argument should be passed to the POSIX function open() when and only when creating a new file.", + "kind": "problem", + "name": "Pass the correct number of arguments to the POSIX open function.", + "precision": "high", + "severity": "error", + "short_name": "CallPOSIXOpenWithCorrectArgumentCount", + "tags": [ + "correctness", + "security" + ], + "implementation_scope": { + "description": "The analysis of invalid parameter count passed to POSIX open calls only applies when the value of the flags argument is computed locally." + } + } + ], + "title": "Call functions with the correct number and type of arguments" + }, + "EXP46-C": { + "properties": { + "obligation": "rule" + }, + "queries": [ + { + "description": "Using bitwise operators with unparenthesized Boolean-like operands may indicate a logic error.", + "kind": "problem", + "name": "Do not use a bitwise operator with a Boolean-like operand", + "precision": "very-high", + "severity": "error", + "short_name": "DoNotUseABitwiseOperatorWithABooleanLikeOperand", + "tags": [ + "maintainability", + "readability" + ] + } + ], + "title": "Do not use a bitwise operator with a Boolean-like operand" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index cbec139e7a..45393411c8 100755 --- a/rules.csv +++ b/rules.csv @@ -483,7 +483,7 @@ c,CERT-C,ARR30-C,Yes,Rule,,,Do not form or use out-of-bounds pointers or array s c,CERT-C,ARR32-C,Yes,Rule,,,Ensure size arguments for variable length arrays are in a valid range,,InvalidMemory,Medium, c,CERT-C,ARR36-C,Yes,Rule,,,Do not subtract or compare two pointers that do not refer to the same array,,Memory,Medium, c,CERT-C,ARR37-C,Yes,Rule,,,Do not add or subtract an integer to a pointer to a non-array object,,InvalidMemory,Medium, -c,CERT-C,ARR38-C,Yes,Rule,,,Guarantee that library functions do not form invalid pointers,,Pointers2,Very Hard, +c,CERT-C,ARR38-C,Yes,Rule,,,Guarantee that library functions do not form invalid pointers,,OutOfBounds,Very Hard, c,CERT-C,ARR39-C,Yes,Rule,,,Do not add or subtract a scaled integer to a pointer,,Pointers2,Medium, c,CERT-C,CON30-C,Yes,Rule,,,Clean up thread-specific storage,,Concurrency4,Very Hard, c,CERT-C,CON31-C,Yes,Rule,,,Do not destroy a mutex while it is locked,CON50-CPP,Concurrency3,Very Hard, @@ -760,7 +760,7 @@ c,MISRA-C-2012,RULE-21-14,Yes,Required,,,The Standard Library function memcmp sh c,MISRA-C-2012,RULE-21-15,Yes,Required,,,"The pointer arguments to the Standard Library functions memcpy, memmove and memcmp shall be pointers to qualified or unqualified versions of compatible types",,Types,Medium, c,MISRA-C-2012,RULE-21-16,Yes,Required,,,"The pointer arguments to the Standard Library function memcmp shall point to either a pointer type, an essentially signed type, an essentially unsigned type, an essentially Boolean type or an essentially enum type",,Types,Medium, c,MISRA-C-2012,RULE-21-17,Yes,Mandatory,,,Use of the string handling functions from shall not result in accesses beyond the bounds of the objects referenced by their pointer parameters,,Memory,Hard, -c,MISRA-C-2012,RULE-21-18,Yes,Mandatory,,,The size_t argument passed to any function in shall have an appropriate value,,Expressions,Medium, +c,MISRA-C-2012,RULE-21-18,Yes,Mandatory,,,The size_t argument passed to any function in shall have an appropriate value,,OutOfBounds,Hard, c,MISRA-C-2012,RULE-21-19,Yes,Mandatory,,,"The pointers returned by the Standard Library functions localeconv, getenv, setlocale or, strerror shall only be used as if they have pointer to const-qualified type",ENV30-C,Contracts,Easy, c,MISRA-C-2012,RULE-21-20,Yes,Mandatory,,,"The pointer returned by the Standard Library functions asctime, ctime, gmtime, localtime, localeconv, getenv, setlocale or strerror shall not be used following a subsequent call to the same function","ENV31-C, ENV34-C",Contracts,Easy, c,MISRA-C-2012,RULE-21-21,Yes,Required,,,The Standard Library function system of shall not be used,ENV33-C,Banned,Import,