Skip to content

Commit c10c65c

Browse files
authored
Merge pull request #1 from jbj/returnstack-EscapesTree
C++: Reimplement cpp/return-stack-allocated-memory with EscapesTree and data flow
2 parents 6a17ebb + 1e0a385 commit c10c65c

File tree

4 files changed

+45
-50
lines changed

4 files changed

+45
-50
lines changed

change-notes/1.20/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
| Use of string copy function in a condition (`cpp/string-copy-return-value-as-boolean`) | correctness | This query identifies calls to string copy functions used in conditions, where it's likely that a different function was intended to be called. |
1212
| Lossy function result cast (`cpp/lossy-function-result-cast`) | correctness | Finds function calls whose result type is a floating point type, which are implicitly cast to an integral type. Newly available but not displayed by default on LGTM. |
1313
| Array argument size mismatch (`cpp/array-arg-size-mismatch`) | reliability | Finds function calls where the size of an array being passed is smaller than the array size of the declared parameter. Newly displayed on LGTM. |
14+
| Returning stack-allocated memory (`cpp/return-stack-allocated-memory`) | reliability, external/cwe/cwe-825 | Finds functions that may return a pointer or reference to stack-allocated memory. This query existed already but has been rewritten from scratch to make the error rate low enough for use on LGTM. Displayed by default. |
1415

1516
## Changes to existing queries
1617

cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,46 @@
66
* @kind problem
77
* @id cpp/return-stack-allocated-memory
88
* @problem.severity warning
9+
* @precision high
910
* @tags reliability
11+
* external/cwe/cwe-825
1012
*/
1113

1214
import cpp
15+
import semmle.code.cpp.dataflow.EscapesTree
16+
import semmle.code.cpp.dataflow.DataFlow
1317

14-
// an expression is possibly stack allocated if it is an aggregate literal
15-
// or accesses a possibly stack allocated local variables
16-
predicate exprMaybeStackAllocated(Expr e) {
17-
e instanceof AggregateLiteral or
18-
varMaybeStackAllocated(e.(VariableAccess).getTarget()) or
19-
exprMayPointToStack(e.(ArrayExpr).getArrayBase())
20-
}
21-
22-
// a local variable is possibly stack allocated if it is not static and
23-
// is initialized to/assigned a possibly stack allocated expression
24-
predicate varMaybeStackAllocated(LocalVariable lv) {
25-
not lv.isStatic() and
26-
not lv.getType() instanceof ReferenceType
18+
/**
19+
* Holds if `n1` may flow to `n2`, ignoring flow through fields because these
20+
* are currently modeled as an overapproximation that assumes all objects may
21+
* alias.
22+
*/
23+
predicate conservativeDataFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
24+
DataFlow::localFlowStep(n1, n2) and
25+
not n2.asExpr() instanceof FieldAccess
2726
}
2827

29-
// an expression possibly points to the stack if it takes the address of
30-
// a possibly stack allocated expression, if it is a reference to a local variable
31-
// that possibly points to the stack, or if it is a possibly stack allocated array
32-
// that is converted (implicitly or explicitly) to a pointer
33-
predicate exprMayPointToStack(Expr e) {
34-
exprMaybeStackAllocated(e.(AddressOfExpr).getAnOperand())
35-
or
36-
varMayPointToStack(e.(VariableAccess).getTarget())
37-
or
28+
from LocalScopeVariable var, VariableAccess va, ReturnStmt r
29+
where
30+
not var.isStatic() and
31+
not var.getType().getUnspecifiedType() instanceof ReferenceType and
32+
not r.isFromUninstantiatedTemplate(_) and
33+
va = var.getAnAccess() and
3834
(
39-
exprMaybeStackAllocated(e) and
40-
e.getType() instanceof ArrayType and
41-
e.getFullyConverted().getType() instanceof PointerType
35+
// To check if the address escapes directly from `e` in `return e`, we need
36+
// to check the fully-converted `e` in case there are implicit
37+
// array-to-pointer conversions or reference conversions.
38+
variableAddressEscapesTree(va, r.getExpr().getFullyConverted())
39+
or
40+
// The data flow library doesn't support conversions, so here we check that
41+
// the address escapes into some expression `pointerToLocal`, which flows
42+
// in a non-trivial way (one or more steps) to a returned expression.
43+
exists(Expr pointerToLocal |
44+
variableAddressEscapesTree(va, pointerToLocal.getFullyConverted()) and
45+
conservativeDataFlowStep+(
46+
DataFlow::exprNode(pointerToLocal),
47+
DataFlow::exprNode(r.getExpr())
48+
)
49+
)
4250
)
43-
}
44-
45-
// a local variable possibly points to the stack if it is initialized to/assigned to
46-
// an expression that possibly points to the stack
47-
predicate varMayPointToStack(LocalVariable lv) {
48-
exprMayPointToStack(lv.getInitializer().getExpr())
49-
or
50-
exists(AssignExpr a |
51-
a.getLValue().(VariableAccess).getTarget() = lv and
52-
exprMayPointToStack(a.getRValue())
53-
)
54-
}
55-
56-
from ReturnStmt r
57-
where exprMayPointToStack(r.getExpr())
58-
select r, "May return stack-allocated memory."
51+
select r, "May return stack-allocated memory from $@.", va, va.toString()
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
| test.cpp:17:2:17:12 | return ... | May return stack-allocated memory. |
2-
| test.cpp:25:2:25:12 | return ... | May return stack-allocated memory. |
3-
| test.cpp:33:2:33:12 | return ... | May return stack-allocated memory. |
4-
| test.cpp:92:2:92:12 | return ... | May return stack-allocated memory. |
5-
| test.cpp:112:2:112:12 | return ... | May return stack-allocated memory. |
6-
| test.cpp:119:2:119:19 | return ... | May return stack-allocated memory. |
1+
| test.cpp:17:2:17:12 | return ... | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc |
2+
| test.cpp:25:2:25:12 | return ... | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc |
3+
| test.cpp:47:2:47:11 | return ... | May return stack-allocated memory from $@. | test.cpp:47:9:47:10 | mc | mc |
4+
| test.cpp:54:2:54:16 | return ... | May return stack-allocated memory from $@. | test.cpp:54:11:54:12 | mc | mc |
5+
| test.cpp:92:2:92:12 | return ... | May return stack-allocated memory from $@. | test.cpp:89:10:89:11 | mc | mc |
6+
| test.cpp:112:2:112:12 | return ... | May return stack-allocated memory from $@. | test.cpp:112:9:112:11 | arr | arr |
7+
| test.cpp:119:2:119:19 | return ... | May return stack-allocated memory from $@. | test.cpp:119:11:119:13 | arr | arr |

cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ MyClass *test3()
3030
MyClass mc;
3131
MyClass *ptr = &mc;
3232
ptr = nullptr;
33-
return ptr; // GOOD [FALSE POSITIVE]
33+
return ptr; // GOOD
3434
}
3535

3636
MyClass *test4()
@@ -44,14 +44,14 @@ MyClass *test4()
4444
MyClass &test5()
4545
{
4646
MyClass mc;
47-
return mc; // BAD [NOT DETECTED]
47+
return mc; // BAD
4848
}
4949

5050
int *test6()
5151
{
5252
MyClass mc;
5353

54-
return &(mc.a); // BAD [NOT DETECTED]
54+
return &(mc.a); // BAD
5555
}
5656

5757
MyClass test7()

0 commit comments

Comments
 (0)