Skip to content

Commit 833d834

Browse files
author
Nikita Kraiouchkine
committed
Implement EXP32-C and EXP36-C
Additionally updated the `EXP36-C` test-case and refactored `MEM57-CPP` to move certain classes and predicates to the `Alignment.qll` library for re-use in `EXP36-C`.
1 parent 1bfa1b1 commit 833d834

11 files changed

+672
-34
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# EXP32-C: Do not access a volatile object through a nonvolatile reference
2+
3+
This query implements the CERT-C rule EXP32-C:
4+
5+
> Do not access a volatile object through a nonvolatile reference
6+
7+
8+
## CERT
9+
10+
** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` **
11+
12+
## Implementation notes
13+
14+
None
15+
16+
## References
17+
18+
* CERT-C: [EXP32-C: Do not access a volatile object through a nonvolatile reference](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* @id c/cert/do-not-access-volatile-object-with-non-volatile-reference
3+
* @name EXP32-C: Do not access a volatile object through a nonvolatile reference
4+
* @description If an an object defined with a volatile-qualified type is referred to with an lvalue
5+
* of a non-volatile-qualified type, the behavior is undefined.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp32-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
17+
/**
18+
* A `Cast` which converts from a pointer to a volatile-qualified type
19+
* to a pointer to a non-volatile-qualified type.
20+
*/
21+
class CastFromVolatileToNonVolatileBaseType extends Cast {
22+
CastFromVolatileToNonVolatileBaseType() {
23+
this.getExpr().getType().(PointerType).getBaseType*().isVolatile() and
24+
this.getActualType() instanceof PointerType and
25+
not this.getActualType().(PointerType).getBaseType*().isVolatile()
26+
}
27+
}
28+
29+
/**
30+
* An `AssignExpr` with an *lvalue* that is a pointer to a volatile base type and
31+
* and *rvalue* that is not also a pointer to a volatile base type.
32+
*/
33+
class NonVolatileObjectAssignedToVolatilePointer extends AssignExpr {
34+
NonVolatileObjectAssignedToVolatilePointer() {
35+
this.getLValue().getType().(DerivedType).getBaseType*().isVolatile() and
36+
not this.getRValue().getUnconverted().getType().(DerivedType).getBaseType*().isVolatile()
37+
}
38+
39+
/**
40+
* All `VariableAccess` expressions which are transitive successors of
41+
* this `Expr` and which access the variable accessed in the *rvalue* of this `Expr`
42+
*/
43+
Expr getASubsequentAccessOfAssignedObject() {
44+
result =
45+
any(VariableAccess va |
46+
va = this.getRValue().getAChild*().(VariableAccess).getTarget().getAnAccess() and
47+
this.getASuccessor+() = va
48+
|
49+
va
50+
)
51+
}
52+
}
53+
54+
from Expr e, string message
55+
where
56+
not isExcluded(e, Pointers3Package::doNotAccessVolatileObjectWithNonVolatileReferenceQuery()) and
57+
(
58+
e instanceof CastFromVolatileToNonVolatileBaseType and
59+
message = "Cast of object with a volatile-qualified type to a non-volatile-qualified type."
60+
or
61+
exists(e.(NonVolatileObjectAssignedToVolatilePointer).getASubsequentAccessOfAssignedObject()) and
62+
message =
63+
"Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type."
64+
)
65+
select e, message
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# EXP36-C: Do not cast pointers into more strictly aligned pointer types
2+
3+
This query implements the CERT-C rule EXP36-C:
4+
5+
> Do not cast pointers into more strictly aligned pointer types
6+
7+
8+
## CERT
9+
10+
** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` **
11+
12+
## Implementation notes
13+
14+
None
15+
16+
## References
17+
18+
* CERT-C: [EXP36-C: Do not cast pointers into more strictly aligned pointer types](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/**
2+
* @id c/cert/do-not-cast-pointer-to-more-strictly-aligned-pointer-type
3+
* @name EXP36-C: Do not cast pointers into more strictly aligned pointer types
4+
* @description Converting a pointer to a different type results in undefined behavior if the
5+
* pointer is not correctly aligned for the new type.
6+
* @kind path-problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp36-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
import codingstandards.cpp.Alignment
17+
import semmle.code.cpp.dataflow.DataFlow
18+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
19+
import DataFlow::PathGraph
20+
21+
/**
22+
* An expression with a type that has defined alignment requirements
23+
*/
24+
abstract class ExprWithAlignment extends Expr {
25+
/**
26+
* Gets the alignment requirements in bytes for the underlying `Expr`
27+
*/
28+
abstract int getAlignment();
29+
30+
/**
31+
* Gets a descriptive string describing the type of expression
32+
*/
33+
abstract string getKind();
34+
}
35+
36+
/**
37+
* A class extending `AddressOfExpr` and `ExprWithAlignment` to reason about the
38+
* alignment of base types addressed with C address-of expressions
39+
*/
40+
class AddressOfAlignedVariableExpr extends AddressOfExpr, ExprWithAlignment {
41+
AddressOfAlignedVariableExpr() { this.getAddressable() instanceof Variable }
42+
43+
AlignAs alignAsAttribute() { result = this.getAddressable().(Variable).getAnAttribute() }
44+
45+
override int getAlignment() {
46+
result = alignAsAttribute().getArgument(0).getValueInt()
47+
or
48+
result = alignAsAttribute().getArgument(0).getValueType().getSize()
49+
or
50+
not exists(alignAsAttribute()) and
51+
result = this.getAddressable().(Variable).getType().getAlignment()
52+
}
53+
54+
override string getKind() { result = "address-of expression" }
55+
}
56+
57+
/**
58+
* A class extending `FunctionCall` and `ExprWithAlignment` to reason about the
59+
* alignment of pointers allocated with calls to C standard library allocation functions
60+
*/
61+
class DefinedAlignmentAllocationExpr extends FunctionCall, ExprWithAlignment {
62+
int alignment;
63+
64+
DefinedAlignmentAllocationExpr() {
65+
this.getTarget().getName() = "aligned_alloc" and
66+
lowerBound(this.getArgument(0)) = upperBound(this.getArgument(0)) and
67+
alignment = upperBound(this.getArgument(0))
68+
or
69+
this.getTarget().getName() = ["malloc", "calloc", "realloc"] and
70+
alignment = getGlobalMaxAlignT()
71+
}
72+
73+
override int getAlignment() { result = alignment }
74+
75+
override string getKind() { result = "call to " + this.getTarget().getName() }
76+
}
77+
78+
/**
79+
* A class extending `VariableAccess` and `ExprWithAlignment` to reason about the
80+
* alignment of pointers accessed based solely on the pointers' base types.
81+
*/
82+
class DefaultAlignedPointerAccessExpr extends VariableAccess, ExprWithAlignment {
83+
DefaultAlignedPointerAccessExpr() {
84+
this.getTarget().getUnspecifiedType() instanceof PointerType and
85+
not this.getTarget().getUnspecifiedType() instanceof VoidPointerType
86+
}
87+
88+
override int getAlignment() {
89+
result = this.getTarget().getType().(PointerType).getBaseType().getAlignment()
90+
}
91+
92+
override string getKind() { result = "pointer base type" }
93+
}
94+
95+
/**
96+
* A data-flow configuration for analysing the flow of `ExprWithAlignment` pointer expressions
97+
* to casts which perform pointer type conversions and potentially create pointer alignment issues.
98+
*/
99+
class ExprWithAlignmentToCStyleCastConfiguration extends DataFlow::Configuration {
100+
ExprWithAlignmentToCStyleCastConfiguration() {
101+
this = "ExprWithAlignmentToCStyleCastConfiguration"
102+
}
103+
104+
override predicate isSource(DataFlow::Node source) {
105+
source.asExpr() instanceof ExprWithAlignment
106+
}
107+
108+
override predicate isSink(DataFlow::Node sink) {
109+
exists(CStyleCast cast |
110+
cast.getUnderlyingType() instanceof PointerType and
111+
cast.getUnconverted() = sink.asExpr()
112+
)
113+
}
114+
}
115+
116+
/**
117+
* A data-flow configuration for tracking flow from `AddressOfExpr` which provide
118+
* most reliable or explicitly defined alignment information to the less reliable
119+
* `DefaultAlignedPointerAccessExpr` expressions.
120+
*
121+
* This data-flow configuration is used
122+
* to exclude an `DefaultAlignedPointerAccessExpr` as a source if a preceding source
123+
* defined by this configuration provides more accurate alignment information.
124+
*/
125+
class AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig extends DataFlow::Configuration {
126+
AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig() {
127+
this = "AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig"
128+
}
129+
130+
override predicate isSource(DataFlow::Node source) {
131+
source.asExpr() instanceof AddressOfAlignedVariableExpr or
132+
source.asExpr() instanceof DefinedAlignmentAllocationExpr
133+
}
134+
135+
override predicate isSink(DataFlow::Node sink) {
136+
sink.asExpr() instanceof DefaultAlignedPointerAccessExpr
137+
}
138+
}
139+
140+
from
141+
DataFlow::PathNode source, DataFlow::PathNode sink, ExprWithAlignment expr, CStyleCast cast,
142+
Type toBaseType, int alignmentFrom, int alignmentTo
143+
where
144+
not isExcluded(cast, Pointers3Package::doNotCastPointerToMoreStrictlyAlignedPointerTypeQuery()) and
145+
any(ExprWithAlignmentToCStyleCastConfiguration config).hasFlowPath(source, sink) and
146+
source.getNode().asExpr() = expr and
147+
sink.getNode().asExpr() = cast.getUnconverted() and
148+
(
149+
// possibility 1: the source node (ExprWithAlignment) is NOT a DefaultAlignedPointerAccessExpr
150+
// meaning that its alignment info is accurate regardless of any preceding ExprWithAlignment nodes
151+
expr instanceof DefaultAlignedPointerAccessExpr
152+
implies
153+
(
154+
// possibility 2: the source node (ExprWithAlignment) IS a DefaultAlignedPointerAccessExpr
155+
// meaning that its alignment info is only accurate if no preceding ExprWithAlignment nodes exist
156+
not any(AllocationOrAddressOfExprToDefaultAlignedPointerAccessConfig config)
157+
.hasFlowTo(source.getNode()) and
158+
expr instanceof DefaultAlignedPointerAccessExpr and
159+
cast.getUnconverted() instanceof VariableAccess
160+
)
161+
) and
162+
toBaseType = cast.getActualType().(PointerType).getBaseType() and
163+
alignmentTo = toBaseType.getAlignment() and
164+
alignmentFrom = expr.getAlignment() and
165+
// only flag cases where the cast's target type has stricter alignment requirements than the source
166+
alignmentFrom < alignmentTo
167+
select sink, source, sink,
168+
"Cast from pointer with " + alignmentFrom +
169+
"-byte alignment (defined by $@) to pointer with base type " + toBaseType.getUnderlyingType() +
170+
" with " + alignmentTo + "-byte alignment.", expr.getUnconverted(), expr.getKind()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| test.c:5:13:5:21 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. |
2+
| test.c:6:13:6:31 | (int *)... | Cast of object with a volatile-qualified type to a non-volatile-qualified type. |
3+
| test.c:14:3:14:55 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. |
4+
| test.c:24:3:25:36 | ... = ... | Non-volatile object referenced via pointer to volatile type and later accessed via its original object of a non-volatile-qualified type. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/EXP32-C/DoNotAccessVolatileObjectWithNonVolatileReference.ql

0 commit comments

Comments
 (0)