@@ -9,6 +9,7 @@ import codingstandards.cpp.Exclusions
9
9
import codingstandards.cpp.Allocations
10
10
import semmle.code.cpp.dataflow.DataFlow
11
11
import semmle.code.cpp.dataflow.DataFlow2
12
+ import DataFlow:: PathGraph
12
13
13
14
/**
14
15
* A pointer to potentially dynamically allocated memory
@@ -38,41 +39,54 @@ class FreeExprSink extends DataFlow::Node {
38
39
}
39
40
40
41
/**
41
- * A data-flow configuration that tracks flow from an `AllocExprSource` to
42
- * the value assigned to a variable.
42
+ * An `Expr` that is an `AddressOfExpr` of a `Variable`.
43
+ *
44
+ * `Field`s of `PointerType` are not included in order to reduce false-positives,
45
+ * as the data-flow library sometimes equates pointers to their underlying data.
43
46
*/
44
- class AllocExprSourceToAssignedValueConfig extends DataFlow2:: Configuration {
45
- AllocExprSourceToAssignedValueConfig ( ) { this = "AllocExprSourceToAssignedValueConfig" }
46
-
47
- override predicate isSource ( DataFlow:: Node source ) { source instanceof AllocExprSource }
48
-
49
- override predicate isSink ( DataFlow:: Node sink ) {
50
- sink .asExpr ( ) = any ( Variable v ) .getAnAssignedValue ( )
51
- }
52
- }
53
-
54
- /**
55
- * An assignment of a value that is not a dynamically allocated pointer to a variable.
56
- */
57
- class NonDynamicallyAllocatedVariableAssignment extends DataFlow:: Node {
58
- NonDynamicallyAllocatedVariableAssignment ( ) {
59
- exists ( Variable v |
60
- this .asExpr ( ) = v .getAnAssignedValue ( ) and
61
- not this .asExpr ( ) instanceof NullValue and
62
- not any ( AllocExprSourceToAssignedValueConfig cfg ) .hasFlowTo ( this )
63
- )
47
+ class AddressOfExprSourceNode extends Expr {
48
+ AddressOfExprSourceNode ( ) {
49
+ exists ( VariableAccess va |
50
+ this .( AddressOfExpr ) .getOperand ( ) = va and
51
+ (
52
+ va .getTarget ( ) instanceof StackVariable or
53
+ va .getTarget ( ) instanceof GlobalVariable or
54
+ // allow address-of field, but only if that field is not a pointer type,
55
+ // as there may be nested allocations assigned to fields of pointer types.
56
+ va .( FieldAccess ) .getTarget ( ) .getUnderlyingType ( ) instanceof ArithmeticType
57
+ )
58
+ or
59
+ this = va and
60
+ exists ( GlobalVariable gv |
61
+ gv = va .getTarget ( ) and
62
+ (
63
+ gv .getUnderlyingType ( ) instanceof ArithmeticType or
64
+ not exists ( gv .getAnAssignedValue ( ) ) or
65
+ exists ( AddressOfExprSourceNode other |
66
+ DataFlow:: localExprFlow ( other , gv .getAnAssignedValue ( ) )
67
+ )
68
+ )
69
+ )
70
+ ) and
71
+ // exclude alloc(&allocated_ptr) cases
72
+ not any ( DynamicMemoryAllocationToAddressOfDefiningArgConfig cfg )
73
+ .hasFlowTo ( DataFlow:: definitionByReferenceNodeFromArgument ( this ) )
64
74
}
65
75
}
66
76
67
77
/**
68
78
* A data-flow configuration that tracks flow from an `AllocExprSource` to a `FreeExprSink`.
69
79
*/
70
- class DynamicMemoryAllocationToFreeConfig extends DataFlow:: Configuration {
71
- DynamicMemoryAllocationToFreeConfig ( ) { this = "DynamicMemoryAllocationToFreeConfig" }
80
+ class DynamicMemoryAllocationToAddressOfDefiningArgConfig extends DataFlow2:: Configuration {
81
+ DynamicMemoryAllocationToAddressOfDefiningArgConfig ( ) {
82
+ this = "DynamicMemoryAllocationToAddressOfDefiningArgConfig"
83
+ }
72
84
73
85
override predicate isSource ( DataFlow:: Node source ) { source instanceof AllocExprSource }
74
86
75
- override predicate isSink ( DataFlow:: Node sink ) { sink instanceof FreeExprSink }
87
+ override predicate isSink ( DataFlow:: Node sink ) {
88
+ sink .asDefiningArgument ( ) instanceof AddressOfExpr
89
+ }
76
90
}
77
91
78
92
/**
@@ -83,14 +97,14 @@ class NonDynamicPointerToFreeConfig extends DataFlow::Configuration {
83
97
NonDynamicPointerToFreeConfig ( ) { this = "NonDynamicPointerToFreeConfig" }
84
98
85
99
override predicate isSource ( DataFlow:: Node source ) {
86
- source instanceof NonDynamicallyAllocatedVariableAssignment
100
+ source . asExpr ( ) instanceof AddressOfExprSourceNode
87
101
}
88
102
89
103
override predicate isSink ( DataFlow:: Node sink ) { sink instanceof FreeExprSink }
90
104
91
105
override predicate isBarrierOut ( DataFlow:: Node node ) {
92
106
// the default interprocedural data-flow model flows through any field or array assignment
93
- // expressionsto the qualifier (array base, pointer dereferenced, or qualifier) instead of the
107
+ // expressions to the qualifier (array base, pointer dereferenced, or qualifier) instead of the
94
108
// individual element or field that the assignment modifies. this default behaviour causes
95
109
// false positives for future frees of the object base, so we remove the edges
96
110
// between those assignments from the graph with `isBarrierOut`.
@@ -103,25 +117,22 @@ class NonDynamicPointerToFreeConfig extends DataFlow::Configuration {
103
117
)
104
118
)
105
119
}
120
+
121
+ override predicate isBarrierIn ( DataFlow:: Node node ) {
122
+ // only the last source expression is relevant
123
+ isSource ( node )
124
+ }
106
125
}
107
126
108
127
abstract class OnlyFreeMemoryAllocatedDynamicallySharedSharedQuery extends Query { }
109
128
110
129
Query getQuery ( ) { result instanceof OnlyFreeMemoryAllocatedDynamicallySharedSharedQuery }
111
130
112
131
query predicate problems (
113
- FreeExprSink free , string message , DataFlow:: Node source , string sourceDescription
132
+ DataFlow :: PathNode element , DataFlow :: PathNode source , DataFlow:: PathNode sink , string message
114
133
) {
115
- not isExcluded ( free .asExpr ( ) , getQuery ( ) ) and
116
- (
117
- not any ( DynamicMemoryAllocationToFreeConfig cfg ) .hasFlowTo ( free ) and
118
- not any ( NonDynamicPointerToFreeConfig cfg ) .hasFlowTo ( free ) and
119
- message = "Free expression frees non-dynamically allocated memory." and
120
- source = free and
121
- sourceDescription = ""
122
- or
123
- any ( NonDynamicPointerToFreeConfig cfg ) .hasFlow ( source , free ) and
124
- message = "Free expression frees $@ which was not dynamically allocated." and
125
- sourceDescription = "memory"
126
- )
134
+ not isExcluded ( element .getNode ( ) .asExpr ( ) , getQuery ( ) ) and
135
+ element = sink and
136
+ any ( NonDynamicPointerToFreeConfig cfg ) .hasFlowPath ( source , sink ) and
137
+ message = "Free expression frees memory which was not dynamically allocated."
127
138
}
0 commit comments