Skip to content

Commit 208a374

Browse files
authored
Merge pull request github#5256 from MathiasVP/promote-insecure-memset-query
C++: Promote insecure removal of memset query
2 parents b7c0d18 + d4f7fab commit 208a374

15 files changed

+664
-398
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* A new query (`cpp/memset-may-be-deleted`) is added to the default query suite. The query finds calls to `memset` that may be removed by the compiler. This behavior can make information-leak vulnerabilities easier to exploit. This query was originally [submitted as an experimental query by @ihsinme](https://github.com/github/codeql/pull/4953).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
char password[MAX_PASSWORD_LENGTH];
2+
// read and verify password
3+
memset(password, 0, MAX_PASSWORD_LENGTH);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
char password[MAX_PASSWORD_LENGTH];
2+
// read and verify password
3+
memset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Calling <code>memset</code> or <code>bzero</code> on a buffer to clear its contents may get optimized
7+
away by the compiler if the buffer is not subsequently used. This is not desirable behavior if the buffer
8+
contains sensitive data that could somehow be retrieved by an attacker.</p>
9+
10+
</overview>
11+
<recommendation>
12+
13+
<p>Use alternative platform-supplied functions that will not get optimized away. Examples of such
14+
functions include <code>memset_s</code>, <code>SecureZeroMemory</code>, and <code>bzero_explicit</code>.
15+
Alternatively, passing the <code>-fno-builtin-memset</code> option to the GCC/Clang compiler usually
16+
also prevents the optimization. Finally, you can use the public-domain <code>secure_memzero</code> function
17+
(see references below). This function, however, is not guaranteed to work on all platforms and compilers.</p>
18+
19+
</recommendation>
20+
<example>
21+
<p>The following program fragment uses <code>memset</code> to erase sensitive information after it is no
22+
longer needed:</p>
23+
<sample src="MemsetMayBeDeleted-bad.c" />
24+
<p>Because of dead store elimination, the call to <code>memset</code> may be removed by the compiler
25+
(since the buffer is not subsequently used), resulting in potentially sensitive data remaining in memory.
26+
</p>
27+
28+
<p>The best solution to this problem is to use the <code>memset_s</code> function instead of
29+
<code>memset</code>:</p>
30+
<sample src="MemsetMayBeDeleted-good.c" />
31+
32+
</example>
33+
<references>
34+
35+
<li>
36+
CERT C Coding Standard:
37+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations">MSC06-C. Beware of compiler optimizations</a>.
38+
</li>
39+
<li>
40+
USENIX: The Advanced Computing Systems Association:
41+
<a href="https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf">Dead Store Elimination (Still) Considered Harmfuls</a>
42+
</li>
43+
44+
</references>
45+
</qhelp>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* @name Call to `memset` may be deleted
3+
* @description Using the `memset` function to clear private data in a variable that has no subsequent use
4+
* can make information-leak vulnerabilities easier to exploit because the compiler can remove the call.
5+
* @kind problem
6+
* @id cpp/memset-may-be-deleted
7+
* @problem.severity warning
8+
* @precision high
9+
* @tags security
10+
* external/cwe/cwe-14
11+
*/
12+
13+
import cpp
14+
import semmle.code.cpp.dataflow.EscapesTree
15+
import semmle.code.cpp.commons.Exclusions
16+
import semmle.code.cpp.models.interfaces.Alias
17+
18+
class MemsetFunction extends Function {
19+
MemsetFunction() {
20+
this.hasGlobalOrStdOrBslName("memset")
21+
or
22+
this.hasGlobalOrStdName("wmemset")
23+
or
24+
this.hasGlobalName(["bzero", "__builtin_memset"])
25+
}
26+
}
27+
28+
predicate isNonEscapingArgument(Expr escaped) {
29+
exists(Call call, AliasFunction aliasFunction, int i |
30+
aliasFunction = call.getTarget() and
31+
call.getArgument(i) = escaped.getUnconverted() and
32+
(
33+
aliasFunction.parameterNeverEscapes(i)
34+
or
35+
aliasFunction.parameterEscapesOnlyViaReturn(i) and
36+
(call instanceof ExprInVoidContext or call.getConversion*() instanceof BoolConversion)
37+
)
38+
)
39+
}
40+
41+
from FunctionCall call, LocalVariable v, MemsetFunction memset
42+
where
43+
call.getTarget() = memset and
44+
not isFromMacroDefinition(call) and
45+
// `v` escapes as the argument to `memset`
46+
variableAddressEscapesTree(v.getAnAccess(), call.getArgument(0).getFullyConverted()) and
47+
// ... and `v` doesn't escape anywhere else.
48+
forall(Expr escape | variableAddressEscapesTree(v.getAnAccess(), escape) |
49+
isNonEscapingArgument(escape)
50+
) and
51+
not v.isStatic() and
52+
// Reference-typed variables get special treatment in `variableAddressEscapesTree` so we leave them
53+
// out of this query.
54+
not v.getUnspecifiedType() instanceof ReferenceType and
55+
// `v` is not only just used in the call to `memset`.
56+
exists(Access acc |
57+
acc = v.getAnAccess() and not call.getArgument(0).getAChild*() = acc and not acc.isUnevaluated()
58+
) and
59+
// There is no later use of `v`.
60+
not v.getAnAccess() = call.getASuccessor*() and
61+
// Not using the `-fno-builtin-memset` flag
62+
exists(Compilation c |
63+
c.getAFileCompiled() = call.getFile() and
64+
not c.getAnArgument() = "-fno-builtin-memset"
65+
)
66+
select call, "Call to " + memset.getName() + " may be deleted by the compiler."

cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.c

Lines changed: 0 additions & 35 deletions
This file was deleted.

cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.qhelp

Lines changed: 0 additions & 31 deletions
This file was deleted.

cpp/ql/src/experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql

Lines changed: 0 additions & 127 deletions
This file was deleted.

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.expected

Lines changed: 0 additions & 3 deletions
This file was deleted.

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-14/semmle/tests/CompilerRemovalOfCodeToClearBuffers.qlref

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)