Skip to content

Commit 7649e87

Browse files
authored
Merge pull request #846 from geoffw0/returnstack
CPP: Improve ReturnStackAllocatedMemory.ql
2 parents a0c12c4 + c10c65c commit 7649e87

5 files changed

Lines changed: 192 additions & 36 deletions

File tree

change-notes/1.20/analysis-cpp.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
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

1718
| **Query** | **Expected impact** | **Change** |
1819
|----------------------------|------------------------|------------------------------------------------------------------|
1920
| Array argument size mismatch (`cpp/array-arg-size-mismatch`) | Fewer false positives | An exception has been added to this query for variable sized arrays. |
21+
| Returning stack-allocated memory (`cpp/return-stack-allocated-memory`) | More correct results | Many more stack allocated expressions are now recognized. |
2022
| Suspicious add with sizeof (`cpp/suspicious-add-sizeof`) | Fewer false positives | Pointer arithmetic on `char * const` expressions (and other variations of `char *`) are now correctly excluded from the results. |
2123
| Suspicious pointer scaling (`cpp/suspicious-pointer-scaling`) | Fewer false positives | False positives involving types that are not uniquely named in the snapshot have been fixed. |
2224
| Call to memory access function may overflow buffer (`cpp/overflow-buffer`) | More correct results | Calls to `fread` are now examined by this query. |

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

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +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
*/
11-
import cpp
12-
13-
// an expression is possibly stack allocated if it is an aggregate literal
14-
// or a reference to a possibly stack allocated local variables
15-
predicate exprMaybeStackAllocated(Expr e) {
16-
e instanceof AggregateLiteral
17-
or varMaybeStackAllocated(e.(VariableAccess).getTarget())
18-
}
19-
20-
// a local variable is possibly stack allocated if it is not static and
21-
// is initialized to/assigned a possibly stack allocated expression
22-
predicate varMaybeStackAllocated(LocalVariable lv) {
23-
not lv.isStatic() and
24-
( lv.getType().getUnderlyingType() instanceof ArrayType
25-
or exprMaybeStackAllocated(lv.getInitializer().getExpr())
26-
or exists(AssignExpr a | a.getLValue().(VariableAccess).getTarget() = lv and
27-
exprMaybeStackAllocated(a.getRValue())))
28-
}
2913

30-
// an expression possibly points to the stack if it takes the address of
31-
// a possibly stack allocated expression, if it is a reference to a local variable
32-
// that possibly points to the stack, or if it is a possibly stack allocated array
33-
// that is converted (implicitly or explicitly) to a pointer
34-
predicate exprMayPointToStack(Expr e) {
35-
e instanceof AddressOfExpr and exprMaybeStackAllocated(e.(AddressOfExpr).getAnOperand())
36-
or varMayPointToStack(e.(VariableAccess).getTarget())
37-
or exprMaybeStackAllocated(e) and e.getType() instanceof ArrayType and e.getFullyConverted().getType() instanceof PointerType
38-
}
14+
import cpp
15+
import semmle.code.cpp.dataflow.EscapesTree
16+
import semmle.code.cpp.dataflow.DataFlow
3917

40-
// a local variable possibly points to the stack if it is initialized to/assigned to
41-
// an expression that possibly points to the stack
42-
predicate varMayPointToStack(LocalVariable lv) {
43-
exprMayPointToStack(lv.getInitializer().getExpr())
44-
or exists(AssignExpr a | a.getLValue().(VariableAccess).getTarget() = lv and
45-
exprMayPointToStack(a.getRValue()))
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
4626
}
4727

48-
from ReturnStmt r
49-
where exprMayPointToStack(r.getExpr())
50-
select r, "May return stack-allocated memory."
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
34+
(
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+
)
50+
)
51+
select r, "May return stack-allocated memory from $@.", va, va.toString()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
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 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
2+
class MyClass
3+
{
4+
public:
5+
int a, b;
6+
};
7+
8+
MyClass makeMyClass()
9+
{
10+
return { 0, 0 }; // GOOD
11+
}
12+
13+
MyClass *test1()
14+
{
15+
MyClass mc;
16+
17+
return &mc; // BAD
18+
}
19+
20+
MyClass *test2()
21+
{
22+
MyClass mc;
23+
MyClass *ptr = &mc;
24+
25+
return ptr; // BAD
26+
}
27+
28+
MyClass *test3()
29+
{
30+
MyClass mc;
31+
MyClass *ptr = &mc;
32+
ptr = nullptr;
33+
return ptr; // GOOD
34+
}
35+
36+
MyClass *test4()
37+
{
38+
MyClass mc;
39+
MyClass &ref = mc;
40+
41+
return &ref; // BAD [NOT DETECTED]
42+
}
43+
44+
MyClass &test5()
45+
{
46+
MyClass mc;
47+
return mc; // BAD
48+
}
49+
50+
int *test6()
51+
{
52+
MyClass mc;
53+
54+
return &(mc.a); // BAD
55+
}
56+
57+
MyClass test7()
58+
{
59+
MyClass mc;
60+
61+
return mc; // GOOD
62+
}
63+
64+
MyClass *test8()
65+
{
66+
MyClass *mc = new MyClass;
67+
68+
return mc; // GOOD
69+
}
70+
71+
MyClass test9()
72+
{
73+
return MyClass(); // GOOD
74+
}
75+
76+
int test10()
77+
{
78+
MyClass mc;
79+
80+
return mc.a; // GOOD
81+
}
82+
83+
MyClass *test11()
84+
{
85+
MyClass *ptr;
86+
87+
{
88+
MyClass mc;
89+
ptr = &mc;
90+
}
91+
92+
return ptr; // BAD
93+
}
94+
95+
MyClass *test12(MyClass *param)
96+
{
97+
return param; // GOOD
98+
}
99+
100+
MyClass *test13()
101+
{
102+
static MyClass mc;
103+
MyClass &ref = mc;
104+
105+
return &ref; // GOOD
106+
}
107+
108+
char *testArray1()
109+
{
110+
char arr[256];
111+
112+
return arr; // BAD
113+
}
114+
115+
char *testArray2()
116+
{
117+
char arr[256];
118+
119+
return &(arr[10]); // BAD
120+
}
121+
122+
char testArray3()
123+
{
124+
char arr[256];
125+
126+
return arr[10]; // GOOD
127+
}
128+
129+
char *testArray4()
130+
{
131+
char arr[256];
132+
char *ptr;
133+
134+
ptr = arr + 1;
135+
ptr++;
136+
137+
return ptr; // BAD [NOT DETECTED]
138+
}
139+
140+
char *testArray5()
141+
{
142+
static char arr[256];
143+
144+
return arr; // GOOD
145+
}

0 commit comments

Comments
 (0)