Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 04a3c3d

Browse files
authored
Merge pull request #4953 from ihsinme/ihsinme-patch-207
CPP: Add query for CWE-14 compiler removal of code to clear buffers.
2 parents aec0e98 + 416aa49 commit 04a3c3d

6 files changed

Lines changed: 398 additions & 0 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// BAD: the memset call will probably be removed.
2+
void getPassword(void) {
3+
char pwd[64];
4+
if (GetPassword(pwd, sizeof(pwd))) {
5+
/* Checking of password, secure operations, etc. */
6+
}
7+
memset(pwd, 0, sizeof(pwd));
8+
}
9+
// GOOD: in this case the memset will not be removed.
10+
void getPassword(void) {
11+
char pwd[64];
12+
13+
if (retrievePassword(pwd, sizeof(pwd))) {
14+
/* Checking of password, secure operations, etc. */
15+
}
16+
memset_s(pwd, 0, sizeof(pwd));
17+
}
18+
// GOOD: in this case the memset will not be removed.
19+
void getPassword(void) {
20+
char pwd[64];
21+
if (retrievePassword(pwd, sizeof(pwd))) {
22+
/* Checking of password, secure operations, etc. */
23+
}
24+
SecureZeroMemory(pwd, sizeof(pwd));
25+
}
26+
// GOOD: in this case the memset will not be removed.
27+
void getPassword(void) {
28+
char pwd[64];
29+
if (retrievePassword(pwd, sizeof(pwd))) {
30+
/* Checking of password, secure operations, etc. */
31+
}
32+
#pragma optimize("", off)
33+
memset(pwd, 0, sizeof(pwd));
34+
#pragma optimize("", on)
35+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Compiler optimization will exclude the cleaning of private information.
7+
Using the <code>memset</code> function to clear private data in a variable that has no subsequent use is potentially dangerous, since the compiler can remove the call.
8+
For some compilers, optimization is also possible when using calls to free memory after the <code>memset</code> function.</p>
9+
10+
<p>It is possible to miss detection of vulnerabilities if used to clear fields of structures or parts of a buffer.</p>
11+
12+
</overview>
13+
<recommendation>
14+
15+
<p>We recommend to use the <code>RtlSecureZeroMemory</code> or <code>memset_s</code> functions, or compilation flags that exclude optimization of <code>memset</code> calls (e.g. -fno-builtin-memset).</p>
16+
17+
</recommendation>
18+
<example>
19+
<p>The following example demonstrates an erroneous and corrected use of the <code>memset</code> function.</p>
20+
<sample src="CompilerRemovalOfCodeToClearBuffers.c" />
21+
22+
</example>
23+
<references>
24+
25+
<li>
26+
CERT C Coding Standard:
27+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations">MSC06-C. Beware of compiler optimizations</a>.
28+
</li>
29+
30+
</references>
31+
</qhelp>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* @name Compiler Removal Of Code To Clear Buffers
3+
* @description Using <code>memset</code> the function to clear private data in a variable that has no subsequent use
4+
* is potentially dangerous because the compiler can remove the call.
5+
* @kind problem
6+
* @id cpp/compiler-removal-of-code-to-clear-buffers
7+
* @problem.severity warning
8+
* @precision medium
9+
* @tags security
10+
* external/cwe/cwe-14
11+
*/
12+
13+
import cpp
14+
import semmle.code.cpp.dataflow.DataFlow
15+
import semmle.code.cpp.dataflow.StackAddress
16+
17+
/**
18+
* A call to `memset` of the form `memset(ptr, value, num)`, for some local variable `ptr`.
19+
*/
20+
class CompilerRemovaMemset extends FunctionCall {
21+
CompilerRemovaMemset() {
22+
this.getTarget().hasGlobalOrStdName("memset") and
23+
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
24+
DataFlow::localFlow(source, sink) and
25+
this.getArgument(0) = isv.getAnAccess() and
26+
(
27+
source.asExpr() = exp
28+
or
29+
// handle the case where exp is defined by an address being passed into some function.
30+
source.asDefiningArgument() = exp
31+
) and
32+
exp.getLocation().getEndLine() < this.getArgument(0).getLocation().getStartLine() and
33+
sink.asExpr() = this.getArgument(0)
34+
)
35+
}
36+
37+
predicate isExistsAllocForThisVariable() {
38+
exists(AllocationExpr alloc, Variable v |
39+
alloc = v.getAnAssignedValue() and
40+
this.getArgument(0) = v.getAnAccess() and
41+
alloc.getASuccessor+() = this
42+
)
43+
or
44+
not stackPointerFlowsToUse(this.getArgument(0), _, _, _)
45+
}
46+
47+
predicate isExistsFreeForThisVariable() {
48+
exists(DeallocationExpr free, Variable v |
49+
this.getArgument(0) = v.getAnAccess() and
50+
free.getFreedExpr() = v.getAnAccess() and
51+
this.getASuccessor+() = free
52+
)
53+
}
54+
55+
predicate isExistsCallWithThisVariableExcludingDeallocationCalls() {
56+
exists(FunctionCall fc, Variable v |
57+
not fc instanceof DeallocationExpr and
58+
this.getArgument(0) = v.getAnAccess() and
59+
fc.getAnArgument() = v.getAnAccess() and
60+
this.getASuccessor+() = fc
61+
)
62+
}
63+
64+
predicate isVariableUseAfterMemsetExcludingCalls() {
65+
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
66+
DataFlow::localFlow(source, sink) and
67+
this.getArgument(0) = isv.getAnAccess() and
68+
source.asExpr() = isv.getAnAccess() and
69+
exp.getLocation().getStartLine() > this.getArgument(2).getLocation().getEndLine() and
70+
not exp.getParent() instanceof FunctionCall and
71+
sink.asExpr() = exp
72+
)
73+
}
74+
75+
predicate isVariableUseBoundWithArgumentFunction() {
76+
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Parameter p, Expr exp |
77+
DataFlow::localFlow(source, sink) and
78+
this.getArgument(0) = isv.getAnAccess() and
79+
this.getEnclosingFunction().getAParameter() = p and
80+
exp.getAChild*() = p.getAnAccess() and
81+
source.asExpr() = exp and
82+
sink.asExpr() = isv.getAnAccess()
83+
)
84+
}
85+
86+
predicate isVariableUseBoundWithGlobalVariable() {
87+
exists(
88+
DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, GlobalVariable gv, Expr exp
89+
|
90+
DataFlow::localFlow(source, sink) and
91+
this.getArgument(0) = isv.getAnAccess() and
92+
exp.getAChild*() = gv.getAnAccess() and
93+
source.asExpr() = exp and
94+
sink.asExpr() = isv.getAnAccess()
95+
)
96+
}
97+
98+
predicate isExistsCompilationFlagsBlockingRemoval() {
99+
exists(Compilation c |
100+
c.getAFileCompiled() = this.getFile() and
101+
c.getAnArgument() = "-fno-builtin-memset"
102+
)
103+
}
104+
105+
predicate isUseVCCompilation() {
106+
exists(Compilation c |
107+
c.getAFileCompiled() = this.getFile() and
108+
(
109+
c.getArgument(2).matches("%gcc%") or
110+
c.getArgument(2).matches("%g++%") or
111+
c.getArgument(2).matches("%clang%") or
112+
c.getArgument(2) = "--force-recompute"
113+
)
114+
)
115+
}
116+
}
117+
118+
from CompilerRemovaMemset fc
119+
where
120+
not (fc.isExistsAllocForThisVariable() and not fc.isExistsFreeForThisVariable()) and
121+
not (fc.isExistsFreeForThisVariable() and not fc.isUseVCCompilation()) and
122+
not fc.isVariableUseAfterMemsetExcludingCalls() and
123+
not fc.isExistsCallWithThisVariableExcludingDeallocationCalls() and
124+
not fc.isVariableUseBoundWithArgumentFunction() and
125+
not fc.isVariableUseBoundWithGlobalVariable() and
126+
not fc.isExistsCompilationFlagsBlockingRemoval()
127+
select fc.getArgument(0), "This variable will not be cleared."
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| test.c:13:9:13:13 | buff1 | This variable will not be cleared. |
2+
| test.c:35:9:35:13 | buff1 | This variable will not be cleared. |
3+
| test.c:43:9:43:13 | buff1 | This variable will not be cleared. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql

0 commit comments

Comments
 (0)