Skip to content

Commit 204acba

Browse files
committed
C++: Add a new query for detecting calls to 'c_str' on temporary objects.
1 parent 22a91d1 commit 204acba

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* @name Use of string after lifetime ends
3+
* @description If the value of a call to 'c_str' outlives the underlying object it may lead to unexpected behavior.
4+
* @kind problem
5+
* @precision high
6+
* @id cpp/use-of-string-after-lifetime-ends
7+
* @problem.severity warning
8+
* @security-severity 8.8
9+
* @tags reliability
10+
* security
11+
* external/cwe/cwe-416
12+
* external/cwe/cwe-664
13+
*/
14+
15+
import cpp
16+
import semmle.code.cpp.models.implementations.StdString
17+
import semmle.code.cpp.models.implementations.StdContainer
18+
19+
/**
20+
* Holds if `e` will be consumed by its parent as a glvalue and does not have
21+
* an lvalue-to-rvalue conversion. This means that it will be materialized into
22+
* a temporary object.
23+
*/
24+
predicate isTemporary(Expr e) {
25+
e.isPRValueCategory() and
26+
e.getUnspecifiedType() instanceof Class and
27+
not e.hasLValueToRValueConversion()
28+
}
29+
30+
/** Holds if `e` is written to a container. */
31+
predicate isStoredInContainer(Expr e) {
32+
exists(StdSequenceContainerInsert insert, Call call, int index |
33+
call = insert.getACallToThisFunction() and
34+
index = insert.getAValueTypeParameterIndex() and
35+
call.getArgument(index) = e
36+
)
37+
or
38+
exists(StdSequenceContainerPush push, Call call, int index |
39+
call = push.getACallToThisFunction() and
40+
index = push.getAValueTypeParameterIndex() and
41+
call.getArgument(index) = e
42+
)
43+
or
44+
exists(StdSequenceEmplace emplace, Call call, int index |
45+
call = emplace.getACallToThisFunction() and
46+
index = emplace.getAValueTypeParameterIndex() and
47+
call.getArgument(index) = e
48+
)
49+
or
50+
exists(StdSequenceEmplaceBack emplaceBack, Call call, int index |
51+
call = emplaceBack.getACallToThisFunction() and
52+
index = emplaceBack.getAValueTypeParameterIndex() and
53+
call.getArgument(index) = e
54+
)
55+
}
56+
57+
/**
58+
* Holds if the value of `e` outlives the enclosing full expression. For
59+
* example, because the value is stored in a local variable.
60+
*/
61+
predicate outlivesFullExpr(Expr e) {
62+
any(Assignment assign).getRValue() = e
63+
or
64+
any(Variable v).getInitializer().getExpr() = e
65+
or
66+
any(ReturnStmt ret).getExpr() = e
67+
or
68+
exists(ConditionalExpr cond |
69+
outlivesFullExpr(cond) and
70+
[cond.getThen(), cond.getElse()] = e
71+
)
72+
or
73+
exists(BinaryOperation bin |
74+
outlivesFullExpr(bin) and
75+
bin.getAnOperand() = e
76+
)
77+
or
78+
exists(ClassAggregateLiteral aggr |
79+
outlivesFullExpr(aggr) and
80+
aggr.getAFieldExpr(_) = e
81+
)
82+
or
83+
exists(ArrayAggregateLiteral aggr |
84+
outlivesFullExpr(aggr) and
85+
aggr.getAnElementExpr(_) = e
86+
)
87+
or
88+
isStoredInContainer(e)
89+
}
90+
91+
from Call c
92+
where
93+
outlivesFullExpr(c) and
94+
not c.isFromUninstantiatedTemplate(_) and
95+
c.getTarget() instanceof StdStringCStr and
96+
isTemporary(c.getQualifier().getFullyConverted())
97+
select c,
98+
"The underlying string object is destroyed after the call to '" + c.getTarget() + "' returns."
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)