-
Notifications
You must be signed in to change notification settings - Fork 1.8k
C++: Add a new query for calling c_str
on temporary objects
#14928
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
22a91d1
C++: Make the sequence container classes public.
MathiasVP 204acba
C++: Add a new query for detecting calls to 'c_str' on temporary obje…
MathiasVP 71ad769
C++: Add qhelp.
MathiasVP e94cde9
C++: Move the use-after-free tests to subdirectory.
MathiasVP e10caa6
C++: Add tests.
MathiasVP ff4c63f
C++: Add change note.
MathiasVP 62c432f
C++: Tabs -> Spaces.
MathiasVP 7b8d164
C++: Add more good test cases.
MathiasVP 2b36ba3
C++: Add support for 'data' in the query.
MathiasVP 8afd928
Apply suggestions from code review
MathiasVP 351caac
C++: Add GOOD and BAD comments to qhelp examples.
MathiasVP File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
cpp/ql/src/Security/CWE/CWE-416/UseOfStringAfterLifetimeEnds.qhelp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<!DOCTYPE qhelp PUBLIC | ||
"-//Semmle//qhelp//EN" | ||
"qhelp.dtd"> | ||
<qhelp> | ||
|
||
<overview> | ||
<p>Calling <code>c_str</code> on a <code>std::string</code> object returns a pointer to the underlying character array. | ||
When the <code>std::string</code> object is destroyed, the pointer returned by <code>c_str</code> is no | ||
longer valid. If the pointer is used after the <code>std::string</code> object is destroyed, then the behavior is undefined. | ||
</p> | ||
</overview> | ||
|
||
<recommendation> | ||
<p> | ||
Ensure that the pointer returned by <code>c_str</code> does not outlive the underlying <code>std::string</code> object. | ||
</p> | ||
</recommendation> | ||
|
||
<example> | ||
<p> | ||
The following example concatenates two <code>std::string</code> objects, and then converts the resulting string to a | ||
C string using <code>c_str</code> so that it can be passed to the <code>work</code> function. | ||
|
||
However, the underlying <code>std::string</code> object that represents the concatenated string is destroyed as soon as the call | ||
to <code>c_str</code> returns. This means that <code>work</code> is given a pointer to invalid memory. | ||
</p> | ||
|
||
<sample src="UseOfStringAfterLifetimeEndsBad.cpp" /> | ||
|
||
<p> | ||
The following example fixes the above code by ensuring that the pointer returned by the call to <code>c_str</code> does | ||
not outlive the underlying <code>std::string</code> objects. This ensures that the pointer passed to <code>work</code> | ||
points to valid memory. | ||
</p> | ||
|
||
<sample src="UseOfStringAfterLifetimeEndsGood.cpp" /> | ||
|
||
</example> | ||
<references> | ||
|
||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory">MEM50-CPP. Do not access freed memory</a>.</li> | ||
|
||
</references> | ||
</qhelp> |
100 changes: 100 additions & 0 deletions
100
cpp/ql/src/Security/CWE/CWE-416/UseOfStringAfterLifetimeEnds.ql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/** | ||
* @name Use of string after lifetime ends | ||
* @description If the value of a call to 'c_str' outlives the underlying object it may lead to unexpected behavior. | ||
* @kind problem | ||
* @precision high | ||
* @id cpp/use-of-string-after-lifetime-ends | ||
* @problem.severity warning | ||
* @security-severity 8.8 | ||
* @tags reliability | ||
* security | ||
* external/cwe/cwe-416 | ||
* external/cwe/cwe-664 | ||
*/ | ||
|
||
import cpp | ||
import semmle.code.cpp.models.implementations.StdString | ||
import semmle.code.cpp.models.implementations.StdContainer | ||
|
||
/** | ||
* Holds if `e` will be consumed by its parent as a glvalue and does not have | ||
* an lvalue-to-rvalue conversion. This means that it will be materialized into | ||
* a temporary object. | ||
*/ | ||
predicate isTemporary(Expr e) { | ||
e instanceof TemporaryObjectExpr | ||
or | ||
e.isPRValueCategory() and | ||
e.getUnspecifiedType() instanceof Class and | ||
not e.hasLValueToRValueConversion() | ||
} | ||
|
||
/** Holds if `e` is written to a container. */ | ||
predicate isStoredInContainer(Expr e) { | ||
exists(StdSequenceContainerInsert insert, Call call, int index | | ||
call = insert.getACallToThisFunction() and | ||
index = insert.getAValueTypeParameterIndex() and | ||
call.getArgument(index) = e | ||
) | ||
or | ||
exists(StdSequenceContainerPush push, Call call, int index | | ||
call = push.getACallToThisFunction() and | ||
index = push.getAValueTypeParameterIndex() and | ||
call.getArgument(index) = e | ||
) | ||
or | ||
exists(StdSequenceEmplace emplace, Call call, int index | | ||
call = emplace.getACallToThisFunction() and | ||
index = emplace.getAValueTypeParameterIndex() and | ||
call.getArgument(index) = e | ||
) | ||
or | ||
exists(StdSequenceEmplaceBack emplaceBack, Call call, int index | | ||
call = emplaceBack.getACallToThisFunction() and | ||
index = emplaceBack.getAValueTypeParameterIndex() and | ||
call.getArgument(index) = e | ||
) | ||
} | ||
|
||
/** | ||
* Holds if the value of `e` outlives the enclosing full expression. For | ||
* example, because the value is stored in a local variable. | ||
*/ | ||
predicate outlivesFullExpr(Expr e) { | ||
any(Assignment assign).getRValue() = e | ||
or | ||
any(Variable v).getInitializer().getExpr() = e | ||
or | ||
any(ReturnStmt ret).getExpr() = e | ||
or | ||
exists(ConditionalExpr cond | | ||
outlivesFullExpr(cond) and | ||
[cond.getThen(), cond.getElse()] = e | ||
) | ||
or | ||
exists(BinaryOperation bin | | ||
outlivesFullExpr(bin) and | ||
bin.getAnOperand() = e | ||
) | ||
or | ||
exists(ClassAggregateLiteral aggr | | ||
outlivesFullExpr(aggr) and | ||
aggr.getAFieldExpr(_) = e | ||
) | ||
or | ||
exists(ArrayAggregateLiteral aggr | | ||
outlivesFullExpr(aggr) and | ||
aggr.getAnElementExpr(_) = e | ||
) | ||
or | ||
isStoredInContainer(e) | ||
} | ||
|
||
from Call c | ||
where | ||
outlivesFullExpr(c) and | ||
not c.isFromUninstantiatedTemplate(_) and | ||
(c.getTarget() instanceof StdStringCStr or c.getTarget() instanceof StdStringData) and | ||
isTemporary(c.getQualifier().getFullyConverted()) | ||
select c, | ||
"The underlying string object is destroyed after the call to '" + c.getTarget() + "' returns." |
9 changes: 9 additions & 0 deletions
9
cpp/ql/src/Security/CWE/CWE-416/UseOfStringAfterLifetimeEndsBad.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include <string> | ||
void work(const char*); | ||
|
||
felicitymay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// BAD: the concatenated string is deallocated when `c_str` returns. So `work` | ||
// is given a pointer to invalid memory. | ||
void work_with_combined_string_bad(std::string s1, std::string s2) { | ||
const char* combined_string = (s1 + s2).c_str(); | ||
work(combined_string); | ||
} |
9 changes: 9 additions & 0 deletions
9
cpp/ql/src/Security/CWE/CWE-416/UseOfStringAfterLifetimeEndsGood.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include <string> | ||
void work(const char*); | ||
|
||
felicitymay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// GOOD: the concatenated string outlives the call to `work`. So the pointer | ||
// obtainted from `c_str` is valid. | ||
void work_with_combined_string_good(std::string s1, std::string s2) { | ||
auto combined_string = s1 + s2; | ||
work(combined_string.c_str()); | ||
} |
4 changes: 4 additions & 0 deletions
4
cpp/ql/src/change-notes/2023-11-28-use-of-string-after-lifetime-ends.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
--- | ||
category: newQuery | ||
--- | ||
* Added a new query, `cpp/use-of-string-after-lifetime-ends`, to detect calls to `c_str` on strings that will be destroyed immediately. |
File renamed without changes.
File renamed without changes.
File renamed without changes.
12 changes: 12 additions & 0 deletions
12
...E/CWE-416/semmle/tests/UseOfStringAfterLifetimeEnds/UseOfStringAfterLifetimeEnds.expected
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
| test.cpp:165:34:165:38 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | | ||
| test.cpp:166:39:166:43 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | | ||
| test.cpp:167:44:167:48 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | | ||
| test.cpp:169:29:169:33 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | | ||
| test.cpp:178:37:178:41 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | | ||
| test.cpp:181:39:181:43 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | | ||
| test.cpp:183:37:183:41 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | | ||
| test.cpp:187:34:187:37 | call to data | The underlying string object is destroyed after the call to 'data' returns. | | ||
| test.cpp:188:39:188:42 | call to data | The underlying string object is destroyed after the call to 'data' returns. | | ||
| test.cpp:189:44:189:47 | call to data | The underlying string object is destroyed after the call to 'data' returns. | | ||
| test.cpp:191:29:191:32 | call to data | The underlying string object is destroyed after the call to 'data' returns. | | ||
| test.cpp:193:31:193:35 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. | |
2 changes: 2 additions & 0 deletions
2
.../CWE/CWE-416/semmle/tests/UseOfStringAfterLifetimeEnds/UseOfStringAfterLifetimeEnds.qlref
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
Security/CWE/CWE-416/UseOfStringAfterLifetimeEnds.ql |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.