@@ -18,157 +18,70 @@ import cpp
1818// recomputing the IR.
1919private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
2020import 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
2223
2324/** Holds if `f` has a name that we intrepret as evidence of intentionally returning the value of the stack pointer. */
2425predicate intentionallyReturnsStackPointer ( Function f ) {
2526 f .getName ( ) .toLowerCase ( ) .matches ( [ "%stack%" , "%sp%" ] )
2627}
2728
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 )
11543 )
116- )
117- }
44+ }
11845
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 ( )
12555 )
12656 }
12757
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+ }
16775}
16876
169- from LocalPathNode source , LocalPathNodeSink sink , VariableAddressInstruction var
77+ from
78+ MustFlowPathNode source , MustFlowPathNode sink , VariableAddressInstruction var ,
79+ ReturnStackAllocatedMemoryConfig conf
17080where
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 ( )
17386select sink .getNode ( ) , source , sink , "May return stack-allocated memory from $@." , var .getAST ( ) ,
17487 var .getAST ( ) .toString ( )
0 commit comments