Skip to content

Commit 2319462

Browse files
author
Nikita Kraiouchkine
authored
Merge pull request #52 from kraiouchkine/kraiouchkine-dev-expressions
Implement C Expressions package and Fix RULE-11-1 false-negative
2 parents 7add99f + 9279d18 commit 2319462

28 files changed

+1454
-11
lines changed

c/cert/src/rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.md

Lines changed: 243 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* @id c/cert/call-posix-open-with-correct-argument-count
3+
* @name EXP37-C: Pass the correct number of arguments to the POSIX open function.
4+
* @description A third argument should be passed to the POSIX function open() when and only when
5+
* creating a new file.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp37-c
10+
* correctness
11+
* security
12+
* external/cert/obligation/rule
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.cert
17+
import semmle.code.cpp.commons.unix.Constants
18+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
19+
20+
int o_creat_val() {
21+
result = any(MacroInvocation ma | ma.getMacroName() = "O_CREAT" | ma.getExpr().getValue().toInt())
22+
or
23+
result = o_creat()
24+
}
25+
26+
/**
27+
* A function call to POSIX open()
28+
*/
29+
class POSIXOpenFunctionCall extends FunctionCall {
30+
POSIXOpenFunctionCall() { this.getTarget().getQualifiedName() = "open" }
31+
32+
/**
33+
* Holds if reasonable bounds exist for the value of the 'flags' argument of the call.
34+
* This predicate will never hold for cases such as wrapper functions
35+
* which pass a parameter to the open() 'flags' argument.
36+
*/
37+
predicate hasFlagsArgBounds() { lowerBound(this.getArgument(1)) >= 0 }
38+
39+
/**
40+
* Holds if the 'flags' argument contains the O_CREAT flag.
41+
* Because this predicate uses the SimpleRangeAnalysis library, it only
42+
* analyzes the bounds of 'flag' arguments which can be deduced locally.
43+
*/
44+
predicate isOpenCreateCall() {
45+
hasFlagsArgBounds() and
46+
upperBound(this.getArgument(1)).(int).bitAnd(o_creat_val()) != 0
47+
}
48+
}
49+
50+
from POSIXOpenFunctionCall call, string message
51+
where
52+
not isExcluded(call, ExpressionsPackage::callPOSIXOpenWithCorrectArgumentCountQuery()) and
53+
// include only analyzable flag arguments which have values that can be determined locally
54+
call.hasFlagsArgBounds() and
55+
// differentiate between two variants:
56+
// 1) a call to open() with the O_CREAT flag set, which should pass three arguments
57+
// 2) a call to open without the O_CREAT flag set, which should pass two arguments
58+
if call.isOpenCreateCall()
59+
then (
60+
call.getNumberOfArguments() != 3 and
61+
message =
62+
"Call to " + call.getTarget().getName() +
63+
" with O_CREAT flag does not pass exactly three arguments."
64+
) else (
65+
call.getNumberOfArguments() != 2 and
66+
message =
67+
"Call to " + call.getTarget().getName() +
68+
" without O_CREAT flag does not pass exactly two arguments."
69+
)
70+
select call, message

c/cert/src/rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.md

Lines changed: 243 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @id c/cert/do-not-call-function-pointer-with-incompatible-type
3+
* @name EXP37-C: Do not call a function pointer which points to a function of an incompatible type
4+
* @description Calling a function pointer with a type incompatible with the function it points to
5+
* is undefined.
6+
* @kind path-problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp37-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
import semmle.code.cpp.dataflow.DataFlow
17+
import DataFlow::PathGraph
18+
19+
/**
20+
* An expression of type `FunctionPointer` which is the unconverted expression of a cast
21+
* which converts the function pointer to a pointer to a function of a different type.
22+
*/
23+
class SuspiciousFunctionPointerCastExpr extends Expr {
24+
SuspiciousFunctionPointerCastExpr() {
25+
exists(CStyleCast cast, Type old, Type new |
26+
this = cast.getUnconverted() and
27+
old = cast.getUnconverted().getUnderlyingType() and
28+
new = cast.getFullyConverted().getUnderlyingType() and
29+
old != new and
30+
old instanceof FunctionPointerType and
31+
new instanceof FunctionPointerType
32+
)
33+
}
34+
}
35+
36+
/**
37+
* Data-flow configuration for flow from a `SuspiciousFunctionPointerCastExpr`
38+
* to a call of the function pointer resulting from the function pointer cast
39+
*/
40+
class SuspectFunctionPointerToCallConfig extends DataFlow::Configuration {
41+
SuspectFunctionPointerToCallConfig() { this = "SuspectFunctionPointerToCallConfig" }
42+
43+
override predicate isSource(DataFlow::Node src) {
44+
src.asExpr() instanceof SuspiciousFunctionPointerCastExpr
45+
}
46+
47+
override predicate isSink(DataFlow::Node sink) {
48+
exists(VariableCall call | sink.asExpr() = call.getExpr().(VariableAccess))
49+
}
50+
}
51+
52+
from
53+
SuspectFunctionPointerToCallConfig config, DataFlow::PathNode src, DataFlow::PathNode sink,
54+
Access access
55+
where
56+
not isExcluded(src.getNode().asExpr(),
57+
ExpressionsPackage::doNotCallFunctionPointerWithIncompatibleTypeQuery()) and
58+
access = src.getNode().asExpr() and
59+
config.hasFlowPath(src, sink)
60+
select src, src, sink,
61+
"Incompatible function $@ assigned to function pointer is eventually called through the pointer.",
62+
access.getTarget(), access.getTarget().getName()

c/cert/src/rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.md

Lines changed: 243 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @id c/cert/do-not-call-functions-with-incompatible-arguments
3+
* @name EXP37-C: Do not pass arguments with an incompatible count or type to a function.
4+
* @description The arguments passed to a function must be compatible with the function's
5+
* parameters.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp37-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
import codingstandards.cpp.MistypedFunctionArguments
17+
18+
from FunctionCall fc, Function f, Parameter p
19+
where
20+
not isExcluded(fc, ExpressionsPackage::doNotCallFunctionsWithIncompatibleArgumentsQuery()) and
21+
(
22+
mistypedFunctionArguments(fc, f, p)
23+
or
24+
complexArgumentPassedToRealParameter(fc, f, p)
25+
)
26+
select fc,
27+
"Argument $@ in call to " + f.toString() + " is incompatible with parameter " + p.getTypedName() +
28+
".", fc.getArgument(p.getIndex()) as arg, arg.toString()
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# EXP46-C: Do not use a bitwise operator with a Boolean-like operand
2+
3+
This query implements the CERT-C rule EXP46-C:
4+
5+
> Do not use a bitwise operator with a Boolean-like operand
6+
7+
8+
## Description
9+
10+
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.
11+
12+
## Noncompliant Code Example
13+
14+
In this noncompliant code example, a bitwise `&` operator is used with the results of an *equality-expression*:
15+
16+
```cpp
17+
if (!(getuid() & geteuid() == 0)) {
18+
/* ... */
19+
}
20+
21+
```
22+
23+
## Compliant Solution
24+
25+
This compliant solution uses the `&&` operator for the logical operation within the conditional expression:
26+
27+
```cpp
28+
if (!(getuid() && geteuid() == 0)) {
29+
/* ... */
30+
}
31+
32+
```
33+
34+
## Risk Assessment
35+
36+
<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> EXP46-C </td> <td> Low </td> <td> Likely </td> <td> Low </td> <td> <strong>P9</strong> </td> <td> <strong>L2</strong> </td> </tr> </tbody> </table>
37+
38+
39+
## Automated Detection
40+
41+
<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> <strong>inappropriate-bool</strong> </td> <td> Supported indirectly via MISRA C:2012 Rule 10.1 </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-EXP46</strong> </td> <td> </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.0p0 </td> <td> <strong>LANG.TYPE.IOT</strong> </td> <td> Inappropriate operand type </td> </tr> <tr> <td> <a> Coverity </a> </td> <td> 2017.07 </td> <td> <strong>CONSTANT_EXPRESSION_RESULT</strong> </td> <td> Partially implemented </td> </tr> <tr> <td> <a> Cppcheck </a> </td> <td> 1.66 </td> <td> cert.py </td> <td> Detected by the addon cert.py </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.2 </td> <td> <strong>C3344, C4502</strong> <strong>C++3709</strong> </td> <td> </td> </tr> <tr> <td> <a> Klocwork </a> </td> <td> 2022.2 </td> <td> <strong>MISRA.LOGIC.OPERATOR.NOT_BOOL</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>136 S</strong> </td> <td> Fully Implemented </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.1 </td> <td> <strong>CERT_C-EXP46-b</strong> </td> <td> Expressions that are effectively Boolean should not be used as operands to operators other than (&amp;&amp;, ||, !, =, ==, !=, ?:) </td> </tr> <tr> <td> <a> PC-lint Plus </a> </td> <td> 1.4 </td> <td> <strong>514</strong> </td> <td> Fully supported </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022a </td> <td> <a> CERT C: Rule EXP46-C </a> </td> <td> Checks for bitwise operations on boolean operands (rule fully covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>3344,4502</strong> </td> <td> </td> </tr> <tr> <td> <a> PRQA QA-C++ </a> </td> <td> 4.4 </td> <td> <strong>3709</strong> </td> <td> </td> </tr> <tr> <td> <a> PVS-Studio </a> </td> <td> 7.20 </td> <td> <strong><a>V564</a>, <a>V1015</a></strong> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>inappropriate-bool</strong> </td> <td> Supported indirectly via MISRA C:2012 Rule 10.1 </td> </tr> </tbody> </table>
42+
43+
44+
## Related Guidelines
45+
46+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
47+
48+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Likely Incorrect Expression \[KOA\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> CWE 2.11 </a> </td> <td> <a> CWE-480 </a> , Use of incorrect operator </td> <td> 2017-07-05: CERT: Rule subset of CWE </td> </tr> <tr> <td> <a> CWE 2.11 </a> </td> <td> <a> CWE-569 </a> </td> <td> 2017-07-06: CERT: Rule subset of CWE </td> </tr> </tbody> </table>
49+
50+
51+
## CERT-CWE Mapping Notes
52+
53+
[Key here](https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152408#HowthisCodingStandardisOrganized-CERT-CWEMappingNotes) for mapping notes
54+
55+
**CWE-480 and EXP46-C**
56+
57+
Intersection( EXP45-C, EXP46-C) = Ø
58+
59+
CWE-480 = Union( EXP46-C, list) where list =
60+
61+
* Usage of incorrect operator besides s/&amp;/&amp;&amp;/ or s/|/||/
62+
63+
## Bibliography
64+
65+
<table> <tbody> <tr> <td> \[ <a> Hatton 1995 </a> \] </td> <td> Section 2.7.2, "Errors of Omission and Addition" </td> </tr> </tbody> </table>
66+
67+
68+
## Implementation notes
69+
70+
None
71+
72+
## References
73+
74+
* CERT-C: [EXP46-C: Do not use a bitwise operator with a Boolean-like operand](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @id c/cert/do-not-use-a-bitwise-operator-with-a-boolean-like-operand
3+
* @name EXP46-C: Do not use a bitwise operator with a Boolean-like operand
4+
* @description Using bitwise operators with unparenthesized Boolean-like operands may indicate a
5+
* logic error.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp46-c
10+
* maintainability
11+
* readability
12+
* external/cert/obligation/rule
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.cert
17+
18+
/**
19+
* Holds if `op` is a bitwise AND, OR, or XOR expression
20+
*/
21+
predicate isBitwiseOperationPotentiallyAmbiguous(BinaryBitwiseOperation op) {
22+
op instanceof BitwiseAndExpr or
23+
op instanceof BitwiseOrExpr or
24+
op instanceof BitwiseXorExpr
25+
}
26+
27+
/**
28+
* Holds if `e` is an unparenthesised boolean expression,
29+
* relational operation, or equality operation.
30+
*/
31+
predicate isDisallowedBitwiseOperationOperand(Expr e) {
32+
not e.isParenthesised() and
33+
(
34+
e.getFullyConverted().getUnderlyingType() instanceof BoolType or
35+
e instanceof RelationalOperation or
36+
e instanceof EqualityOperation
37+
)
38+
}
39+
40+
from Expr operand, Operation operation
41+
where
42+
not isExcluded(operation,
43+
ExpressionsPackage::doNotUseABitwiseOperatorWithABooleanLikeOperandQuery()) and
44+
isBitwiseOperationPotentiallyAmbiguous(operation) and
45+
operand = operation.getAnOperand() and
46+
isDisallowedBitwiseOperationOperand(operand)
47+
select operation,
48+
"Bitwise operator " + operation.getOperator() +
49+
" performs potentially unintended operation on $@.", operand, "boolean operand"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
| test.c:18:3:18:6 | call to open | Call to open without O_CREAT flag does not pass exactly two arguments. |
2+
| test.c:19:3:19:6 | call to open | Call to open with O_CREAT flag does not pass exactly three arguments. |
3+
| test.c:23:3:23:6 | call to open | Call to open without O_CREAT flag does not pass exactly two arguments. |
4+
| test.c:26:3:26:6 | call to open | Call to open with O_CREAT flag does not pass exactly three arguments. |
5+
| test.c:34:3:34:6 | call to open | Call to open without O_CREAT flag does not pass exactly two arguments. |
6+
| test.c:35:3:35:6 | call to open | Call to open with O_CREAT flag does not pass exactly three arguments. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/EXP37-C/CallPOSIXOpenWithCorrectArgumentCount.ql
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
edges
2+
| test.c:48:68:48:70 | fns [f1] | test.c:49:3:49:5 | fns [f1] |
3+
| test.c:49:3:49:5 | fns [f1] | test.c:49:8:49:9 | f1 |
4+
| test.c:61:28:61:29 | f2 | test.c:62:3:62:11 | v1_called |
5+
| test.c:73:3:73:5 | fns [post update] [f1] | test.c:75:45:75:48 | & ... [f1] |
6+
| test.c:73:3:73:13 | ... = ... | test.c:73:3:73:5 | fns [post update] [f1] |
7+
| test.c:73:12:73:13 | v2 | test.c:73:3:73:13 | ... = ... |
8+
| test.c:75:45:75:48 | & ... [f1] | test.c:48:68:48:70 | fns [f1] |
9+
nodes
10+
| test.c:48:68:48:70 | fns [f1] | semmle.label | fns [f1] |
11+
| test.c:49:3:49:5 | fns [f1] | semmle.label | fns [f1] |
12+
| test.c:49:8:49:9 | f1 | semmle.label | f1 |
13+
| test.c:61:28:61:29 | f2 | semmle.label | f2 |
14+
| test.c:62:3:62:11 | v1_called | semmle.label | v1_called |
15+
| test.c:70:9:70:17 | v3_called | semmle.label | v3_called |
16+
| test.c:73:3:73:5 | fns [post update] [f1] | semmle.label | fns [post update] [f1] |
17+
| test.c:73:3:73:13 | ... = ... | semmle.label | ... = ... |
18+
| test.c:73:12:73:13 | v2 | semmle.label | v2 |
19+
| test.c:75:45:75:48 | & ... [f1] | semmle.label | & ... [f1] |
20+
subpaths
21+
#select
22+
| 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 |
23+
| 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 |
24+
| 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 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/EXP37-C/DoNotCallFunctionPointerWithIncompatibleType.ql
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| 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 |
2+
| 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 |
3+
| 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 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/EXP37-C/DoNotCallFunctionsWithIncompatibleArguments.ql

0 commit comments

Comments
 (0)