@@ -18,157 +18,70 @@ import cpp
18
18
// recomputing the IR.
19
19
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
20
20
import semmle.code.cpp.ir.IR
21
- import semmle.code.cpp.ir.dataflow.DataFlow:: DataFlow
21
+ import semmle.code.cpp.ir.dataflow.MustFlow
22
+ import PathGraph
22
23
23
24
/** Holds if `f` has a name that we intrepret as evidence of intentionally returning the value of the stack pointer. */
24
25
predicate intentionallyReturnsStackPointer ( Function f ) {
25
26
f .getName ( ) .toLowerCase ( ) .matches ( [ "%stack%" , "%sp%" ] )
26
27
}
27
28
28
- /**
29
- * Holds if `source` is a node that represents the use of a stack variable
30
- */
31
- predicate isSource ( Node source ) {
32
- exists ( VariableAddressInstruction var , Function func |
33
- var = source .asInstruction ( ) and
34
- func = var .getEnclosingFunction ( ) and
35
- var .getASTVariable ( ) instanceof StackVariable and
36
- // Pointer-to-member types aren't properly handled in the dbscheme.
37
- not var .getResultType ( ) instanceof PointerToMemberType and
38
- // Rule out FPs caused by extraction errors.
39
- not any ( ErrorExpr e ) .getEnclosingFunction ( ) = func and
40
- not intentionallyReturnsStackPointer ( func )
41
- )
42
- }
43
-
44
- /**
45
- * Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in
46
- * a `ReturnValueInstruction`. We use the `StoreInstruction` instead of the instruction that defines the
47
- * `ReturnValueInstruction`'s source value oprand because the former has better location information.
48
- */
49
- predicate isSink ( Node sink ) {
50
- exists ( StoreInstruction store |
51
- store .getDestinationAddress ( ) .( VariableAddressInstruction ) .getIRVariable ( ) instanceof
52
- IRReturnVariable and
53
- sink .asOperand ( ) = store .getSourceValueOperand ( )
54
- )
55
- }
56
-
57
- /** Holds if `node1` _must_ flow to `node2`. */
58
- predicate step ( Node node1 , Node node2 ) {
59
- instructionToOperandStep ( node1 .asInstruction ( ) , node2 .asOperand ( ) )
60
- or
61
- operandToInstructionStep ( node1 .asOperand ( ) , node2 .asInstruction ( ) )
62
- }
63
-
64
- predicate instructionToOperandStep ( Instruction instr , Operand operand ) { operand .getDef ( ) = instr }
65
-
66
- /**
67
- * Holds if `operand` flows to the result of `instr`.
68
- *
69
- * This predicate ignores flow through `PhiInstruction`s to create a 'must flow' relation. It also
70
- * intentionally conflates addresses of fields and their object, and pointer offsets with their
71
- * base pointer as this allows us to detect cases where an object's address flows to a return statement
72
- * via a field. For example:
73
- *
74
- * ```cpp
75
- * struct S { int x, y };
76
- * int* test() {
77
- * S s;
78
- * return &s.x; // BAD: &s.x is an address of a variable on the stack.
79
- * }
80
- * ```
81
- */
82
- predicate operandToInstructionStep ( Operand operand , Instruction instr ) {
83
- instr .( CopyInstruction ) .getSourceValueOperand ( ) = operand
84
- or
85
- instr .( ConvertInstruction ) .getUnaryOperand ( ) = operand
86
- or
87
- instr .( CheckedConvertOrNullInstruction ) .getUnaryOperand ( ) = operand
88
- or
89
- instr .( InheritanceConversionInstruction ) .getUnaryOperand ( ) = operand
90
- or
91
- instr .( FieldAddressInstruction ) .getObjectAddressOperand ( ) = operand
92
- or
93
- instr .( PointerOffsetInstruction ) .getLeftOperand ( ) = operand
94
- }
95
-
96
- /** Holds if a source node flows to `n`. */
97
- predicate branchlessLocalFlow0 ( Node n ) {
98
- isSource ( n )
99
- or
100
- exists ( Node mid |
101
- branchlessLocalFlow0 ( mid ) and
102
- step ( mid , n )
103
- )
104
- }
105
-
106
- /** Holds if `n` is reachable through some source node, and `n` also eventually reaches a sink. */
107
- predicate branchlessLocalFlow1 ( Node n ) {
108
- branchlessLocalFlow0 ( n ) and
109
- (
110
- isSink ( n )
111
- or
112
- exists ( Node mid |
113
- branchlessLocalFlow1 ( mid ) and
114
- step ( n , mid )
29
+ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
30
+ ReturnStackAllocatedMemoryConfig ( ) { this = "ReturnStackAllocatedMemoryConfig" }
31
+
32
+ override predicate isSource ( DataFlow:: Node source ) {
33
+ // Holds if `source` is a node that represents the use of a stack variable
34
+ exists ( VariableAddressInstruction var , Function func |
35
+ var = source .asInstruction ( ) and
36
+ func = var .getEnclosingFunction ( ) and
37
+ var .getASTVariable ( ) instanceof StackVariable and
38
+ // Pointer-to-member types aren't properly handled in the dbscheme.
39
+ not var .getResultType ( ) instanceof PointerToMemberType and
40
+ // Rule out FPs caused by extraction errors.
41
+ not any ( ErrorExpr e ) .getEnclosingFunction ( ) = func and
42
+ not intentionallyReturnsStackPointer ( func )
115
43
)
116
- )
117
- }
44
+ }
118
45
119
- newtype TLocalPathNode =
120
- TLocalPathNodeMid ( Node n ) {
121
- branchlessLocalFlow1 ( n ) and
122
- (
123
- isSource ( n ) or
124
- exists ( LocalPathNodeMid mid | step ( mid .getNode ( ) , n ) )
46
+ override predicate isSink ( DataFlow:: Node sink ) {
47
+ // Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in
48
+ // a `ReturnValueInstruction`.
49
+ // We use the `StoreInstruction` instead of the instruction that defines the
50
+ // `ReturnValueInstruction`'s source value oprand because the former has better location information.
51
+ exists ( StoreInstruction store |
52
+ store .getDestinationAddress ( ) .( VariableAddressInstruction ) .getIRVariable ( ) instanceof
53
+ IRReturnVariable and
54
+ sink .asOperand ( ) = store .getSourceValueOperand ( )
125
55
)
126
56
}
127
57
128
- abstract class LocalPathNode extends TLocalPathNode {
129
- Node n ;
130
-
131
- /** Gets the underlying node. */
132
- Node getNode ( ) { result = n }
133
-
134
- /** Gets a textual representation of this node. */
135
- string toString ( ) { result = n .toString ( ) }
136
-
137
- /** Gets the location of this element. */
138
- Location getLocation ( ) { result = n .getLocation ( ) }
139
-
140
- /** Gets a successor `LocalPathNode`, if any. */
141
- LocalPathNode getASuccessor ( ) { step ( this .getNode ( ) , result .getNode ( ) ) }
142
- }
143
-
144
- class LocalPathNodeMid extends LocalPathNode , TLocalPathNodeMid {
145
- LocalPathNodeMid ( ) { this = TLocalPathNodeMid ( n ) }
146
- }
147
-
148
- class LocalPathNodeSink extends LocalPathNodeMid {
149
- LocalPathNodeSink ( ) { isSink ( this .getNode ( ) ) }
150
- }
151
-
152
- /**
153
- * Holds if `source` is a source node, `sink` is a sink node, and there's flow
154
- * from `source` to `sink` using `step` relation.
155
- */
156
- predicate hasFlow ( LocalPathNode source , LocalPathNodeSink sink ) {
157
- isSource ( source .getNode ( ) ) and
158
- source .getASuccessor + ( ) = sink
159
- }
160
-
161
- predicate reach ( LocalPathNode n ) { n instanceof LocalPathNodeSink or reach ( n .getASuccessor ( ) ) }
162
-
163
- query predicate edges ( LocalPathNode a , LocalPathNode b ) { a .getASuccessor ( ) = b and reach ( b ) }
164
-
165
- query predicate nodes ( LocalPathNode n , string key , string val ) {
166
- reach ( n ) and key = "semmle.label" and val = n .toString ( )
58
+ /**
59
+ * This configuration intentionally conflates addresses of fields and their object, and pointer offsets
60
+ * with their base pointer as this allows us to detect cases where an object's address flows to a
61
+ * return statement via a field. For example:
62
+ *
63
+ * ```cpp
64
+ * struct S { int x, y };
65
+ * int* test() {
66
+ * S s;
67
+ * return &s.x; // BAD: &s.x is an address of a variable on the stack.
68
+ * }
69
+ */
70
+ override predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
71
+ node2 .asInstruction ( ) .( FieldAddressInstruction ) .getObjectAddressOperand ( ) = node1 .asOperand ( )
72
+ or
73
+ node2 .asInstruction ( ) .( PointerOffsetInstruction ) .getLeftOperand ( ) = node1 .asOperand ( )
74
+ }
167
75
}
168
76
169
- from LocalPathNode source , LocalPathNodeSink sink , VariableAddressInstruction var
77
+ from
78
+ MustFlowPathNode source , MustFlowPathNode sink , VariableAddressInstruction var ,
79
+ ReturnStackAllocatedMemoryConfig conf
170
80
where
171
- hasFlow ( source , sink ) and
172
- source .getNode ( ) .asInstruction ( ) = var
81
+ conf .hasFlowPath ( source , sink ) and
82
+ source .getNode ( ) .asInstruction ( ) = var and
83
+ // Only raise an alert if we're returning from the _same_ callable as the on that
84
+ // declared the stack variable.
85
+ var .getEnclosingFunction ( ) = sink .getNode ( ) .getEnclosingCallable ( )
173
86
select sink .getNode ( ) , source , sink , "May return stack-allocated memory from $@." , var .getAST ( ) ,
174
87
var .getAST ( ) .toString ( )
0 commit comments