mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'main' into java/merge-5226
This commit is contained in:
3
cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-bad.c
Normal file
3
cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted-bad.c
Normal file
@@ -0,0 +1,3 @@
|
||||
char password[MAX_PASSWORD_LENGTH];
|
||||
// read and verify password
|
||||
memset(password, 0, MAX_PASSWORD_LENGTH);
|
||||
@@ -0,0 +1,3 @@
|
||||
char password[MAX_PASSWORD_LENGTH];
|
||||
// read and verify password
|
||||
memset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);
|
||||
45
cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.qhelp
Normal file
45
cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.qhelp
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Calling <code>memset</code> or <code>bzero</code> on a buffer to clear its contents may get optimized
|
||||
away by the compiler if the buffer is not subsequently used. This is not desirable behavior if the buffer
|
||||
contains sensitive data that could somehow be retrieved by an attacker.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Use alternative platform-supplied functions that will not get optimized away. Examples of such
|
||||
functions include <code>memset_s</code>, <code>SecureZeroMemory</code>, and <code>bzero_explicit</code>.
|
||||
Alternatively, passing the <code>-fno-builtin-memset</code> option to the GCC/Clang compiler usually
|
||||
also prevents the optimization. Finally, you can use the public-domain <code>secure_memzero</code> function
|
||||
(see references below). This function, however, is not guaranteed to work on all platforms and compilers.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following program fragment uses <code>memset</code> to erase sensitive information after it is no
|
||||
longer needed:</p>
|
||||
<sample src="MemsetMayBeDeleted-bad.c" />
|
||||
<p>Because of dead store elimination, the call to <code>memset</code> may be removed by the compiler
|
||||
(since the buffer is not subsequently used), resulting in potentially sensitive data remaining in memory.
|
||||
</p>
|
||||
|
||||
<p>The best solution to this problem is to use the <code>memset_s</code> function instead of
|
||||
<code>memset</code>:</p>
|
||||
<sample src="MemsetMayBeDeleted-good.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations">MSC06-C. Beware of compiler optimizations</a>.
|
||||
</li>
|
||||
<li>
|
||||
USENIX: The Advanced Computing Systems Association:
|
||||
<a href="https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf">Dead Store Elimination (Still) Considered Harmfuls</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
66
cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql
Normal file
66
cpp/ql/src/Security/CWE/CWE-014/MemsetMayBeDeleted.ql
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @name Call to `memset` may be deleted
|
||||
* @description Using the `memset` function to clear private data in a variable that has no subsequent use
|
||||
* can make information-leak vulnerabilities easier to exploit because the compiler can remove the call.
|
||||
* @kind problem
|
||||
* @id cpp/memset-may-be-deleted
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-14
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.EscapesTree
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
|
||||
class MemsetFunction extends Function {
|
||||
MemsetFunction() {
|
||||
this.hasGlobalOrStdOrBslName("memset")
|
||||
or
|
||||
this.hasGlobalOrStdName("wmemset")
|
||||
or
|
||||
this.hasGlobalName(["bzero", "__builtin_memset"])
|
||||
}
|
||||
}
|
||||
|
||||
predicate isNonEscapingArgument(Expr escaped) {
|
||||
exists(Call call, AliasFunction aliasFunction, int i |
|
||||
aliasFunction = call.getTarget() and
|
||||
call.getArgument(i) = escaped.getUnconverted() and
|
||||
(
|
||||
aliasFunction.parameterNeverEscapes(i)
|
||||
or
|
||||
aliasFunction.parameterEscapesOnlyViaReturn(i) and
|
||||
(call instanceof ExprInVoidContext or call.getConversion*() instanceof BoolConversion)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from FunctionCall call, LocalVariable v, MemsetFunction memset
|
||||
where
|
||||
call.getTarget() = memset and
|
||||
not isFromMacroDefinition(call) and
|
||||
// `v` escapes as the argument to `memset`
|
||||
variableAddressEscapesTree(v.getAnAccess(), call.getArgument(0).getFullyConverted()) and
|
||||
// ... and `v` doesn't escape anywhere else.
|
||||
forall(Expr escape | variableAddressEscapesTree(v.getAnAccess(), escape) |
|
||||
isNonEscapingArgument(escape)
|
||||
) and
|
||||
not v.isStatic() and
|
||||
// Reference-typed variables get special treatment in `variableAddressEscapesTree` so we leave them
|
||||
// out of this query.
|
||||
not v.getUnspecifiedType() instanceof ReferenceType and
|
||||
// `v` is not only just used in the call to `memset`.
|
||||
exists(Access acc |
|
||||
acc = v.getAnAccess() and not call.getArgument(0).getAChild*() = acc and not acc.isUnevaluated()
|
||||
) and
|
||||
// There is no later use of `v`.
|
||||
not v.getAnAccess() = call.getASuccessor*() and
|
||||
// Not using the `-fno-builtin-memset` flag
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = call.getFile() and
|
||||
not c.getAnArgument() = "-fno-builtin-memset"
|
||||
)
|
||||
select call, "Call to " + memset.getName() + " may be deleted by the compiler."
|
||||
@@ -1,35 +0,0 @@
|
||||
// BAD: the memset call will probably be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
if (GetPassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
memset(pwd, 0, sizeof(pwd));
|
||||
}
|
||||
// GOOD: in this case the memset will not be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
|
||||
if (retrievePassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
memset_s(pwd, 0, sizeof(pwd));
|
||||
}
|
||||
// GOOD: in this case the memset will not be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
if (retrievePassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
SecureZeroMemory(pwd, sizeof(pwd));
|
||||
}
|
||||
// GOOD: in this case the memset will not be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
if (retrievePassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
#pragma optimize("", off)
|
||||
memset(pwd, 0, sizeof(pwd));
|
||||
#pragma optimize("", on)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Compiler optimization will exclude the cleaning of private information.
|
||||
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.
|
||||
For some compilers, optimization is also possible when using calls to free memory after the <code>memset</code> function.</p>
|
||||
|
||||
<p>It is possible to miss detection of vulnerabilities if used to clear fields of structures or parts of a buffer.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<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>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of the <code>memset</code> function.</p>
|
||||
<sample src="CompilerRemovalOfCodeToClearBuffers.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations">MSC06-C. Beware of compiler optimizations</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,127 +0,0 @@
|
||||
/**
|
||||
* @name Compiler Removal Of Code To Clear Buffers
|
||||
* @description Using <code>memset</code> the function to clear private data in a variable that has no subsequent use
|
||||
* is potentially dangerous because the compiler can remove the call.
|
||||
* @kind problem
|
||||
* @id cpp/compiler-removal-of-code-to-clear-buffers
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-14
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.StackAddress
|
||||
|
||||
/**
|
||||
* A call to `memset` of the form `memset(ptr, value, num)`, for some local variable `ptr`.
|
||||
*/
|
||||
class CompilerRemovaMemset extends FunctionCall {
|
||||
CompilerRemovaMemset() {
|
||||
this.getTarget().hasGlobalOrStdName("memset") and
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
(
|
||||
source.asExpr() = exp
|
||||
or
|
||||
// handle the case where exp is defined by an address being passed into some function.
|
||||
source.asDefiningArgument() = exp
|
||||
) and
|
||||
exp.getLocation().getEndLine() < this.getArgument(0).getLocation().getStartLine() and
|
||||
sink.asExpr() = this.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsAllocForThisVariable() {
|
||||
exists(AllocationExpr alloc, Variable v |
|
||||
alloc = v.getAnAssignedValue() and
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
alloc.getASuccessor+() = this
|
||||
)
|
||||
or
|
||||
not stackPointerFlowsToUse(this.getArgument(0), _, _, _)
|
||||
}
|
||||
|
||||
predicate isExistsFreeForThisVariable() {
|
||||
exists(DeallocationExpr free, Variable v |
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
free.getFreedExpr() = v.getAnAccess() and
|
||||
this.getASuccessor+() = free
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsCallWithThisVariableExcludingDeallocationCalls() {
|
||||
exists(FunctionCall fc, Variable v |
|
||||
not fc instanceof DeallocationExpr and
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
fc.getAnArgument() = v.getAnAccess() and
|
||||
this.getASuccessor+() = fc
|
||||
)
|
||||
}
|
||||
|
||||
predicate isVariableUseAfterMemsetExcludingCalls() {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
source.asExpr() = isv.getAnAccess() and
|
||||
exp.getLocation().getStartLine() > this.getArgument(2).getLocation().getEndLine() and
|
||||
not exp.getParent() instanceof FunctionCall and
|
||||
sink.asExpr() = exp
|
||||
)
|
||||
}
|
||||
|
||||
predicate isVariableUseBoundWithArgumentFunction() {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Parameter p, Expr exp |
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
this.getEnclosingFunction().getAParameter() = p and
|
||||
exp.getAChild*() = p.getAnAccess() and
|
||||
source.asExpr() = exp and
|
||||
sink.asExpr() = isv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isVariableUseBoundWithGlobalVariable() {
|
||||
exists(
|
||||
DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, GlobalVariable gv, Expr exp
|
||||
|
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
exp.getAChild*() = gv.getAnAccess() and
|
||||
source.asExpr() = exp and
|
||||
sink.asExpr() = isv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsCompilationFlagsBlockingRemoval() {
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = this.getFile() and
|
||||
c.getAnArgument() = "-fno-builtin-memset"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isUseVCCompilation() {
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = this.getFile() and
|
||||
(
|
||||
c.getArgument(2).matches("%gcc%") or
|
||||
c.getArgument(2).matches("%g++%") or
|
||||
c.getArgument(2).matches("%clang%") or
|
||||
c.getArgument(2) = "--force-recompute"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from CompilerRemovaMemset fc
|
||||
where
|
||||
not (fc.isExistsAllocForThisVariable() and not fc.isExistsFreeForThisVariable()) and
|
||||
not (fc.isExistsFreeForThisVariable() and not fc.isUseVCCompilation()) and
|
||||
not fc.isVariableUseAfterMemsetExcludingCalls() and
|
||||
not fc.isExistsCallWithThisVariableExcludingDeallocationCalls() and
|
||||
not fc.isVariableUseBoundWithArgumentFunction() and
|
||||
not fc.isVariableUseBoundWithGlobalVariable() and
|
||||
not fc.isExistsCompilationFlagsBlockingRemoval()
|
||||
select fc.getArgument(0), "This variable will not be cleared."
|
||||
@@ -80,11 +80,9 @@ class Element extends ElementBase {
|
||||
File getFile() { result = this.getLocation().getFile() }
|
||||
|
||||
/**
|
||||
* Holds if this element may be from source.
|
||||
*
|
||||
* Note: this predicate is provided for consistency with the libraries
|
||||
* for other languages, such as Java and Python. In C++, all files are
|
||||
* classified as source files, so this predicate is always true.
|
||||
* Holds if this element may be from source. This predicate holds for all
|
||||
* elements, except for those in the dummy file, whose name is the empty string.
|
||||
* The dummy file contains declarations that are built directly into the compiler.
|
||||
*/
|
||||
predicate fromSource() { this.getFile().fromSource() }
|
||||
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -415,6 +415,30 @@ private module Cached {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `fromNode` to `toNode` because they are the post-update
|
||||
* nodes of some function output and input respectively, where the output and input
|
||||
* are aliases. A typical example is a function returning `this`, implementing a fluent
|
||||
* interface.
|
||||
*/
|
||||
cached
|
||||
predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
|
||||
exists(Node fromPre, Node toPre |
|
||||
fromPre = fromNode.getPreUpdateNode() and
|
||||
toPre = toNode.getPreUpdateNode()
|
||||
|
|
||||
exists(DataFlowCall c |
|
||||
// Does the language-specific simpleLocalFlowStep already model flow
|
||||
// from function input to output?
|
||||
fromPre = getAnOutNode(c, _) and
|
||||
toPre.(ArgumentNode).argumentOf(c, _) and
|
||||
simpleLocalFlowStep(toPre.(ArgumentNode), fromPre)
|
||||
)
|
||||
or
|
||||
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
|
||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||
*/
|
||||
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
|
||||
simpleLocalFlowStep(node1, node2) and
|
||||
(
|
||||
simpleLocalFlowStep(node1, node2) or
|
||||
reverseStepThroughInputOutputAlias(node1, node2)
|
||||
) and
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
|
||||
@@ -415,6 +415,30 @@ private module Cached {
|
||||
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `fromNode` to `toNode` because they are the post-update
|
||||
* nodes of some function output and input respectively, where the output and input
|
||||
* are aliases. A typical example is a function returning `this`, implementing a fluent
|
||||
* interface.
|
||||
*/
|
||||
cached
|
||||
predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
|
||||
exists(Node fromPre, Node toPre |
|
||||
fromPre = fromNode.getPreUpdateNode() and
|
||||
toPre = toNode.getPreUpdateNode()
|
||||
|
|
||||
exists(DataFlowCall c |
|
||||
// Does the language-specific simpleLocalFlowStep already model flow
|
||||
// from function input to output?
|
||||
fromPre = getAnOutNode(c, _) and
|
||||
toPre.(ArgumentNode).argumentOf(c, _) and
|
||||
simpleLocalFlowStep(toPre.(ArgumentNode), fromPre)
|
||||
)
|
||||
or
|
||||
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
|
||||
@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
private newtype TOperand =
|
||||
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
|
||||
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
|
||||
not Construction::isInCycle(useInstr) and
|
||||
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
|
||||
} or
|
||||
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
|
||||
useInstr.getOpcode().hasOperand(tag)
|
||||
} or
|
||||
TPhiOperand(
|
||||
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
|
||||
}
|
||||
private import Imports::TOperand
|
||||
private import internal.OperandInternal
|
||||
|
||||
/**
|
||||
* Base class for all register operands. This is a placeholder for the IPA union type that we will
|
||||
* eventually use for this purpose.
|
||||
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
|
||||
* of `TOperand` that are used in this stage.
|
||||
*/
|
||||
private class RegisterOperandBase extends TRegisterOperand {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the register operand with the specified parameters.
|
||||
*/
|
||||
private RegisterOperandBase registerOperand(
|
||||
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
|
||||
) {
|
||||
result = TRegisterOperand(useInstr, tag, defInstr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
|
||||
* will eventually use for this purpose.
|
||||
*/
|
||||
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the non-Phi memory operand with the specified parameters.
|
||||
*/
|
||||
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
|
||||
result = TNonPhiMemoryOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
|
||||
* eventually use for this purpose.
|
||||
*/
|
||||
private class PhiOperandBase extends TPhiOperand {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
private PhiOperandBase phiOperand(
|
||||
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
private class TStageOperand =
|
||||
TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
|
||||
* (the defining instruction) in another instruction (the use instruction)
|
||||
*/
|
||||
class Operand extends TOperand {
|
||||
class Operand extends TStageOperand {
|
||||
cached
|
||||
Operand() {
|
||||
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _)
|
||||
) or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Operand" }
|
||||
|
||||
@@ -238,9 +191,11 @@ class Operand extends TOperand {
|
||||
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
cached
|
||||
MemoryOperand() {
|
||||
this instanceof NonPhiMemoryOperandBase or
|
||||
this instanceof PhiOperandBase
|
||||
this instanceof TNonSSAMemoryOperand or
|
||||
this instanceof TPhiOperand or
|
||||
this instanceof TChiOperand
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
|
||||
|
||||
NonPhiOperand() {
|
||||
this = registerOperand(useInstr, tag, _) or
|
||||
this = nonPhiMemoryOperand(useInstr, tag)
|
||||
this = nonSSAMemoryOperand(useInstr, tag) or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override Instruction getUse() { result = useInstr }
|
||||
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
|
||||
/**
|
||||
* An operand that consumes a register (non-memory) result.
|
||||
*/
|
||||
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
|
||||
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
|
||||
override RegisterOperandTag tag;
|
||||
Instruction defInstr;
|
||||
|
||||
cached
|
||||
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
|
||||
/**
|
||||
* A memory operand other than the operand of a `Phi` instruction.
|
||||
*/
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
|
||||
override MemoryOperandTag tag;
|
||||
|
||||
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
|
||||
cached
|
||||
NonPhiMemoryOperand() {
|
||||
this = nonSSAMemoryOperand(useInstr, tag)
|
||||
or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
|
||||
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
|
||||
class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
PhiInstruction useInstr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
@@ -2,3 +2,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
private import semmle.code.cpp.ir.implementation.internal.TOperand
|
||||
import AliasedSSAOperands
|
||||
@@ -6,6 +6,7 @@ private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import SSAOperands
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
|
||||
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
|
||||
|
||||
@@ -5,3 +5,4 @@ import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import AliasedSSA as Alias
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand::AliasedSSAOperands as SSAOperands
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
private import TInstruction
|
||||
private import OperandTag
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawConstruction
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedConstruction
|
||||
private import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedConstruction
|
||||
private import semmle.code.cpp.ir.implementation.raw.IR as Raw
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Unaliased
|
||||
private import semmle.code.cpp.ir.implementation.aliased_ssa.IR as Aliased
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
/**
|
||||
* Provides the newtype used to represent operands across all phases of the IR.
|
||||
*/
|
||||
private module Internal {
|
||||
/**
|
||||
* An IR operand. `TOperand` is shared across all phases of the IR. There are branches of this
|
||||
* type for operands created directly from the AST (`TRegisterOperand` and `TNonSSAMemoryOperand`),
|
||||
* for operands computed by each stage of SSA construction (`T*PhiOperand` and
|
||||
* `TAliasedChiOperand`), and a placehold branch for operands that do not exist in a given
|
||||
* stage of IR construction (`TNoOperand`).
|
||||
*/
|
||||
cached
|
||||
newtype TOperand =
|
||||
// RAW
|
||||
TRegisterOperand(TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr) {
|
||||
defInstr = RawConstruction::getRegisterOperandDefinition(useInstr, tag) and
|
||||
not RawConstruction::isInCycle(useInstr) and
|
||||
strictcount(RawConstruction::getRegisterOperandDefinition(useInstr, tag)) = 1
|
||||
} or
|
||||
// Placeholder for Phi and Chi operands in stages that don't have the corresponding instructions
|
||||
TNoOperand() { none() } or
|
||||
// Can be "removed" later when there's unreachable code
|
||||
// These operands can be reused across all three stages. They just get different defs.
|
||||
TNonSSAMemoryOperand(Raw::Instruction useInstr, MemoryOperandTag tag) {
|
||||
// Has no definition in raw but will get definitions later
|
||||
useInstr.getOpcode().hasOperand(tag)
|
||||
} or
|
||||
TUnaliasedPhiOperand(
|
||||
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
|
||||
Unaliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
|
||||
} or
|
||||
//// ALIASED
|
||||
////
|
||||
// Until we share SSA, these will be all the phis there are. With SSA
|
||||
// sharing, these will add to the ones that are already there.
|
||||
// If we share SSA, be careful with the case where we remove all possible
|
||||
// indirect writes to a variable because they're dead code. In that case it's
|
||||
// important that we use the same definition of "is variable aliased" across
|
||||
// the phases.
|
||||
TAliasedPhiOperand(
|
||||
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
|
||||
Aliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
|
||||
} or
|
||||
TAliasedChiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Reexports some branches from `TOperand` so they can be used in stage modules without importing
|
||||
* `TOperand` itself.
|
||||
*/
|
||||
private module Shared {
|
||||
class TRegisterOperand = Internal::TRegisterOperand;
|
||||
|
||||
/**
|
||||
* Returns the register operand with the specified parameters.
|
||||
*/
|
||||
TRegisterOperand registerOperand(
|
||||
TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr
|
||||
) {
|
||||
result = Internal::TRegisterOperand(useInstr, tag, defInstr)
|
||||
}
|
||||
|
||||
class TNonSSAMemoryOperand = Internal::TNonSSAMemoryOperand;
|
||||
|
||||
/**
|
||||
* Returns the non-Phi memory operand with the specified parameters.
|
||||
*/
|
||||
TNonSSAMemoryOperand nonSSAMemoryOperand(TRawInstruction useInstr, MemoryOperandTag tag) {
|
||||
result = Internal::TNonSSAMemoryOperand(useInstr, tag)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
|
||||
* raw IR stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module RawOperands {
|
||||
import Shared
|
||||
|
||||
class TPhiOperand = Internal::TNoOperand;
|
||||
|
||||
class TChiOperand = Internal::TNoOperand;
|
||||
|
||||
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand phiOperand(
|
||||
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
|
||||
Overlap overlap
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
TChiOperand chiOperand(Raw::Instruction useInstr, ChiOperandTag tag) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
|
||||
* unaliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module UnaliasedSSAOperands {
|
||||
import Shared
|
||||
|
||||
class TPhiOperand = Internal::TUnaliasedPhiOperand;
|
||||
|
||||
class TChiOperand = Internal::TNoOperand;
|
||||
|
||||
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand phiOperand(
|
||||
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
|
||||
Unaliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
TChiOperand chiOperand(Unaliased::Instruction useInstr, ChiOperandTag tag) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
|
||||
* asliased SSA stage.
|
||||
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
|
||||
* a class alias.
|
||||
*/
|
||||
module AliasedSSAOperands {
|
||||
import Shared
|
||||
|
||||
class TPhiOperand = Internal::TAliasedPhiOperand;
|
||||
|
||||
class TChiOperand = Internal::TAliasedChiOperand;
|
||||
|
||||
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
TPhiOperand phiOperand(
|
||||
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
|
||||
Aliased::IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Chi operand with the specified parameters.
|
||||
*/
|
||||
TChiOperand chiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) {
|
||||
result = Internal::TAliasedChiOperand(useInstr, tag)
|
||||
}
|
||||
}
|
||||
@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
private newtype TOperand =
|
||||
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
|
||||
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
|
||||
not Construction::isInCycle(useInstr) and
|
||||
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
|
||||
} or
|
||||
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
|
||||
useInstr.getOpcode().hasOperand(tag)
|
||||
} or
|
||||
TPhiOperand(
|
||||
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
|
||||
}
|
||||
private import Imports::TOperand
|
||||
private import internal.OperandInternal
|
||||
|
||||
/**
|
||||
* Base class for all register operands. This is a placeholder for the IPA union type that we will
|
||||
* eventually use for this purpose.
|
||||
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
|
||||
* of `TOperand` that are used in this stage.
|
||||
*/
|
||||
private class RegisterOperandBase extends TRegisterOperand {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the register operand with the specified parameters.
|
||||
*/
|
||||
private RegisterOperandBase registerOperand(
|
||||
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
|
||||
) {
|
||||
result = TRegisterOperand(useInstr, tag, defInstr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
|
||||
* will eventually use for this purpose.
|
||||
*/
|
||||
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the non-Phi memory operand with the specified parameters.
|
||||
*/
|
||||
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
|
||||
result = TNonPhiMemoryOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
|
||||
* eventually use for this purpose.
|
||||
*/
|
||||
private class PhiOperandBase extends TPhiOperand {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
private PhiOperandBase phiOperand(
|
||||
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
private class TStageOperand =
|
||||
TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
|
||||
* (the defining instruction) in another instruction (the use instruction)
|
||||
*/
|
||||
class Operand extends TOperand {
|
||||
class Operand extends TStageOperand {
|
||||
cached
|
||||
Operand() {
|
||||
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _)
|
||||
) or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Operand" }
|
||||
|
||||
@@ -238,9 +191,11 @@ class Operand extends TOperand {
|
||||
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
cached
|
||||
MemoryOperand() {
|
||||
this instanceof NonPhiMemoryOperandBase or
|
||||
this instanceof PhiOperandBase
|
||||
this instanceof TNonSSAMemoryOperand or
|
||||
this instanceof TPhiOperand or
|
||||
this instanceof TChiOperand
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
|
||||
|
||||
NonPhiOperand() {
|
||||
this = registerOperand(useInstr, tag, _) or
|
||||
this = nonPhiMemoryOperand(useInstr, tag)
|
||||
this = nonSSAMemoryOperand(useInstr, tag) or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override Instruction getUse() { result = useInstr }
|
||||
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
|
||||
/**
|
||||
* An operand that consumes a register (non-memory) result.
|
||||
*/
|
||||
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
|
||||
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
|
||||
override RegisterOperandTag tag;
|
||||
Instruction defInstr;
|
||||
|
||||
cached
|
||||
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
|
||||
/**
|
||||
* A memory operand other than the operand of a `Phi` instruction.
|
||||
*/
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
|
||||
override MemoryOperandTag tag;
|
||||
|
||||
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
|
||||
cached
|
||||
NonPhiMemoryOperand() {
|
||||
this = nonSSAMemoryOperand(useInstr, tag)
|
||||
or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
|
||||
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
|
||||
class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
PhiInstruction useInstr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
@@ -2,3 +2,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
private import semmle.code.cpp.ir.implementation.internal.TOperand
|
||||
import RawOperands
|
||||
@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
private newtype TOperand =
|
||||
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
|
||||
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
|
||||
not Construction::isInCycle(useInstr) and
|
||||
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
|
||||
} or
|
||||
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
|
||||
useInstr.getOpcode().hasOperand(tag)
|
||||
} or
|
||||
TPhiOperand(
|
||||
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
|
||||
}
|
||||
private import Imports::TOperand
|
||||
private import internal.OperandInternal
|
||||
|
||||
/**
|
||||
* Base class for all register operands. This is a placeholder for the IPA union type that we will
|
||||
* eventually use for this purpose.
|
||||
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
|
||||
* of `TOperand` that are used in this stage.
|
||||
*/
|
||||
private class RegisterOperandBase extends TRegisterOperand {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the register operand with the specified parameters.
|
||||
*/
|
||||
private RegisterOperandBase registerOperand(
|
||||
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
|
||||
) {
|
||||
result = TRegisterOperand(useInstr, tag, defInstr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
|
||||
* will eventually use for this purpose.
|
||||
*/
|
||||
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the non-Phi memory operand with the specified parameters.
|
||||
*/
|
||||
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
|
||||
result = TNonPhiMemoryOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
|
||||
* eventually use for this purpose.
|
||||
*/
|
||||
private class PhiOperandBase extends TPhiOperand {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Phi operand with the specified parameters.
|
||||
*/
|
||||
private PhiOperandBase phiOperand(
|
||||
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
|
||||
) {
|
||||
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
|
||||
}
|
||||
private class TStageOperand =
|
||||
TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
|
||||
|
||||
/**
|
||||
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
|
||||
* (the defining instruction) in another instruction (the use instruction)
|
||||
*/
|
||||
class Operand extends TOperand {
|
||||
class Operand extends TStageOperand {
|
||||
cached
|
||||
Operand() {
|
||||
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
|
||||
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
|
||||
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
|
||||
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
|
||||
this = phiOperand(use, def, predecessorBlock, _)
|
||||
) or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Operand" }
|
||||
|
||||
@@ -238,9 +191,11 @@ class Operand extends TOperand {
|
||||
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
cached
|
||||
MemoryOperand() {
|
||||
this instanceof NonPhiMemoryOperandBase or
|
||||
this instanceof PhiOperandBase
|
||||
this instanceof TNonSSAMemoryOperand or
|
||||
this instanceof TPhiOperand or
|
||||
this instanceof TChiOperand
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
|
||||
|
||||
NonPhiOperand() {
|
||||
this = registerOperand(useInstr, tag, _) or
|
||||
this = nonPhiMemoryOperand(useInstr, tag)
|
||||
this = nonSSAMemoryOperand(useInstr, tag) or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override Instruction getUse() { result = useInstr }
|
||||
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
|
||||
/**
|
||||
* An operand that consumes a register (non-memory) result.
|
||||
*/
|
||||
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
|
||||
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
|
||||
override RegisterOperandTag tag;
|
||||
Instruction defInstr;
|
||||
|
||||
cached
|
||||
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
|
||||
/**
|
||||
* A memory operand other than the operand of a `Phi` instruction.
|
||||
*/
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
|
||||
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
|
||||
override MemoryOperandTag tag;
|
||||
|
||||
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
|
||||
cached
|
||||
NonPhiMemoryOperand() {
|
||||
this = nonSSAMemoryOperand(useInstr, tag)
|
||||
or
|
||||
this = chiOperand(useInstr, tag)
|
||||
}
|
||||
|
||||
final override string toString() { result = tag.toString() }
|
||||
|
||||
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
|
||||
class PhiInputOperand extends MemoryOperand, TPhiOperand {
|
||||
PhiInstruction useInstr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
Overlap overlap;
|
||||
|
||||
cached
|
||||
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
|
||||
@@ -2,3 +2,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
private import semmle.code.cpp.ir.implementation.internal.TOperand
|
||||
import UnaliasedSSAOperands
|
||||
@@ -6,6 +6,7 @@ private import Imports::Overlap
|
||||
private import Imports::TInstruction
|
||||
private import Imports::RawIR as RawIR
|
||||
private import SSAInstructions
|
||||
private import SSAOperands
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
|
||||
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand
|
||||
|
||||
@@ -6,3 +6,4 @@ import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
|
||||
import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
import semmle.code.cpp.ir.implementation.internal.TOperand::UnaliasedSSAOperands as SSAOperands
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
| test.c:13:9:13:13 | buff1 | This variable will not be cleared. |
|
||||
| test.c:35:9:35:13 | buff1 | This variable will not be cleared. |
|
||||
| test.c:43:9:43:13 | buff1 | This variable will not be cleared. |
|
||||
@@ -1 +0,0 @@
|
||||
experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql
|
||||
@@ -1,201 +0,0 @@
|
||||
struct buffers
|
||||
{
|
||||
unsigned char buff1[50];
|
||||
unsigned char *buff2;
|
||||
} globalBuff1,*globalBuff2;
|
||||
|
||||
unsigned char * globalBuff;
|
||||
void badFunc0_0(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc0_0(){
|
||||
unsigned char buff1[12];
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc0_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
memset(buff1,12,12);
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
free(buff1);
|
||||
}
|
||||
void nobadFunc1_0(){
|
||||
unsigned char * buff1;
|
||||
buff1 = (unsigned char *) malloc(12);
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void badFunc1_0(){
|
||||
unsigned char * buff1;
|
||||
buff1 = (unsigned char *) malloc(12);
|
||||
memset(buff1,12,12);
|
||||
free(buff1);
|
||||
}
|
||||
void badFunc1_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
free(buff1);
|
||||
}
|
||||
void nobadFunc2_0_0(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(buff1);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(buff1+3);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_2(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(*buff1);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_3(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(*(buff1+3));
|
||||
}
|
||||
unsigned char * nobadFunc2_0_4(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return buff1;
|
||||
}
|
||||
|
||||
unsigned char * nobadFunc2_0_5(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return buff1+3;
|
||||
}
|
||||
unsigned char nobadFunc2_0_6(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return *buff1;
|
||||
}
|
||||
|
||||
unsigned char nobadFunc2_0_7(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return *(buff1+3);
|
||||
}
|
||||
void nobadFunc2_1_0(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
if(*buff1==0)
|
||||
printf("123123");
|
||||
}
|
||||
void nobadFunc2_1_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
if(*(buff1+3)==0)
|
||||
printf("123123");
|
||||
}
|
||||
void nobadFunc2_1_2(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
buff1[2]=5;
|
||||
}
|
||||
void nobadFunc3_0(unsigned char * buffAll){
|
||||
unsigned char * buff1 = buffAll;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_1(unsigned char * buffAll){
|
||||
unsigned char * buff1 = buffAll+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_2(struct buffers buffAll){
|
||||
unsigned char * buff1 = buffAll.buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_3(struct buffers buffAll){
|
||||
unsigned char * buff1 = buffAll.buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_4(struct buffers buffAll){
|
||||
unsigned char * buff1 = buffAll.buff2+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_5(struct buffers * buffAll){
|
||||
unsigned char * buff1 = buffAll->buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_6(struct buffers *buffAll){
|
||||
unsigned char * buff1 = buffAll->buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4(){
|
||||
unsigned char * buff1 = globalBuff;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_0(){
|
||||
unsigned char * buff1 = globalBuff;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_1(){
|
||||
unsigned char * buff1 = globalBuff+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_2(){
|
||||
unsigned char * buff1 = globalBuff1.buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_3(){
|
||||
unsigned char * buff1 = globalBuff1.buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_4(){
|
||||
unsigned char * buff1 = globalBuff1.buff2+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_5(){
|
||||
unsigned char * buff1 = globalBuff2->buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_6(){
|
||||
unsigned char * buff1 = globalBuff2->buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.cpp:48:5:48:10 | call to memset | Call to memset may be deleted by the compiler. |
|
||||
| test.cpp:79:5:79:10 | call to memset | Call to memset may be deleted by the compiler. |
|
||||
| test.cpp:208:2:208:7 | call to memset | Call to memset may be deleted by the compiler. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-014/MemsetMayBeDeleted.ql
|
||||
140
cpp/ql/test/query-tests/Security/CWE/CWE-014/test.c
Normal file
140
cpp/ql/test/query-tests/Security/CWE/CWE-014/test.c
Normal file
@@ -0,0 +1,140 @@
|
||||
typedef unsigned long long size_t;
|
||||
void *memset(void *s, int c, unsigned long n);
|
||||
void *__builtin_memset(void *s, int c, unsigned long n);
|
||||
typedef int errno_t;
|
||||
typedef unsigned int rsize_t;
|
||||
errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count);
|
||||
char *strcpy(char *dest, const char *src);
|
||||
|
||||
extern void use_pw(char *pw);
|
||||
|
||||
#define PW_SIZE 32
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): deleted
|
||||
int func1(void) {
|
||||
char pw1[PW_SIZE];
|
||||
use_pw(pw1);
|
||||
memset(pw1, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
int func1a(void) {
|
||||
char pw1a[PW_SIZE];
|
||||
use_pw(pw1a);
|
||||
__builtin_memset(pw1a, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): deleted
|
||||
char *func1b(void) {
|
||||
char pw1b[PW_SIZE];
|
||||
use_pw(pw1b);
|
||||
memset(pw1b, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
pw1b[0] = pw1b[3] = 'a';
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: not deleted
|
||||
// x86-64 clang 9.0.0: not deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
int func1c(char pw1c[PW_SIZE]) {
|
||||
use_pw(pw1c);
|
||||
memset(pw1c, 0, PW_SIZE); // GOOD
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: not deleted
|
||||
// x86-64 clang 9.0.0: not deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
char pw1d[PW_SIZE];
|
||||
int func1d() {
|
||||
use_pw(pw1d);
|
||||
memset(pw1d, 0, PW_SIZE); // GOOD
|
||||
return 0;
|
||||
}
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): deleted
|
||||
char *func2(void) {
|
||||
char pw2[PW_SIZE];
|
||||
use_pw(pw2);
|
||||
memset(pw2, 1, PW_SIZE); // BAD [NOT DETECTED]
|
||||
return pw2;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): partially deleted
|
||||
int func3(void) {
|
||||
char pw3[PW_SIZE];
|
||||
use_pw(pw3);
|
||||
memset(pw3, 4, PW_SIZE); // BAD [NOT DETECTED]
|
||||
return pw3[2];
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
int func4(void) {
|
||||
char pw1a[PW_SIZE];
|
||||
use_pw(pw1a);
|
||||
__builtin_memset(pw1a + 3, 0, PW_SIZE - 3); // BAD [NOT DETECTED]
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
int func6(void) {
|
||||
char pw1a[PW_SIZE];
|
||||
use_pw(pw1a);
|
||||
__builtin_memset(&pw1a[3], 0, PW_SIZE - 3); // BAD [NOT DETECTED]
|
||||
return pw1a[2];
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
int func5(void) {
|
||||
char pw1a[PW_SIZE];
|
||||
use_pw(pw1a);
|
||||
__builtin_memset(pw1a + 3, 0, PW_SIZE - 4); // GOOD
|
||||
return pw1a[4];
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
int func7(void) {
|
||||
char pw1a[PW_SIZE];
|
||||
use_pw(pw1a);
|
||||
__builtin_memset(&pw1a[3], 0, PW_SIZE - 5); // BAD [NOT DETECTED]
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: not deleted
|
||||
// x86-64 clang 9.0.0: not deleted
|
||||
// x64 msvc v19.14 (WINE): not deleted
|
||||
int func8(void) {
|
||||
char pw1a[PW_SIZE];
|
||||
use_pw(pw1a);
|
||||
__builtin_memset(pw1a + pw1a[3], 0, PW_SIZE - 4); // GOOD
|
||||
return pw1a[4];
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.14 (WINE): deleted
|
||||
char *func9(void) {
|
||||
char pw1[PW_SIZE];
|
||||
use_pw(pw1);
|
||||
memset(pw1, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
return 0;
|
||||
}
|
||||
401
cpp/ql/test/query-tests/Security/CWE/CWE-014/test.cpp
Normal file
401
cpp/ql/test/query-tests/Security/CWE/CWE-014/test.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
extern "C" {
|
||||
typedef unsigned long long size_t;
|
||||
void *memset(void *s, int c, unsigned long n);
|
||||
void *__builtin_memset(void *s, int c, unsigned long n);
|
||||
typedef int errno_t;
|
||||
typedef unsigned int rsize_t;
|
||||
errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count);
|
||||
char *strcpy(char *dest, const char *src);
|
||||
void *memcpy(void *dest, const void *src, unsigned long n);
|
||||
void *malloc(unsigned long size);
|
||||
void free(void *ptr);
|
||||
extern void use_pw(char *pw);
|
||||
int printf(const char* format, ...);
|
||||
char* gets(char * str);
|
||||
}
|
||||
|
||||
#define PW_SIZE 32
|
||||
|
||||
struct mem {
|
||||
int a;
|
||||
char b[PW_SIZE];
|
||||
int c;
|
||||
};
|
||||
|
||||
// x86-64 gcc 9.2: not deleted
|
||||
// x86-64 clang 9.0.0: not deleted
|
||||
// x64 msvc v19.22: not deleted
|
||||
void func(char buff[128], unsigned long long sz) {
|
||||
gets(buff);
|
||||
memset(buff, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: not deleted
|
||||
// x86-64 clang 9.0.0: not deleted
|
||||
// x64 msvc v19.22: not deleted
|
||||
char *func2(char buff[128], unsigned long long sz) {
|
||||
gets(buff);
|
||||
memset(buff, 0, PW_SIZE); // GOOD
|
||||
return buff;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: deleted
|
||||
void func3(unsigned long long sz) {
|
||||
char buff[128];
|
||||
gets(buff);
|
||||
memset(buff, 0, PW_SIZE); // BAD
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: deleted
|
||||
void func4(unsigned long long sz) {
|
||||
char buff[128];
|
||||
gets(buff);
|
||||
memset(buff, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
strcpy(buff, "Hello");
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: deleted
|
||||
void func5(unsigned long long sz) {
|
||||
char buff[128];
|
||||
gets(buff);
|
||||
memset(buff, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
if (sz > 5) {
|
||||
strcpy(buff, "Hello");
|
||||
}
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: deleted
|
||||
void func6(unsigned long long sz) {
|
||||
struct mem m;
|
||||
gets(m.b);
|
||||
memset(&m, 0, PW_SIZE); // BAD
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: deleted
|
||||
void func7(unsigned long long sz) {
|
||||
struct mem m;
|
||||
gets(m.b);
|
||||
memset(&m, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
m.a = 15;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: not deleted
|
||||
void func8(unsigned long long sz) {
|
||||
struct mem *m = (struct mem *)malloc(sizeof(struct mem));
|
||||
gets(m->b);
|
||||
memset(m, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: not deleted
|
||||
void func9(unsigned long long sz) {
|
||||
struct mem *m = (struct mem *)malloc(sizeof(struct mem));
|
||||
gets(m->b);
|
||||
memset(m, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
free(m);
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: not deleted
|
||||
void func10(unsigned long long sz) {
|
||||
struct mem *m = (struct mem *)malloc(sizeof(struct mem));
|
||||
gets(m->b);
|
||||
memset(m, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
m->a = sz;
|
||||
m->c = m->a + 1;
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: deleted
|
||||
// x86-64 clang 9.0.0: deleted
|
||||
// x64 msvc v19.22: not deleted
|
||||
void func11(unsigned long long sz) {
|
||||
struct mem *m = (struct mem *)malloc(sizeof(struct mem));
|
||||
gets(m->b);
|
||||
::memset(m, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
if (sz > 5) {
|
||||
strcpy(m->b, "Hello");
|
||||
}
|
||||
}
|
||||
|
||||
// x86-64 gcc 9.2: not deleted
|
||||
// x86-64 clang 9.0.0: not deleted
|
||||
// x64 msvc v19.22: not deleted
|
||||
int func12(unsigned long long sz) {
|
||||
struct mem *m = (struct mem *)malloc(sizeof(struct mem));
|
||||
gets(m->b);
|
||||
memset(m, 0, sz); // GOOD
|
||||
return m->c;
|
||||
}
|
||||
|
||||
int funcN1() {
|
||||
char pw[PW_SIZE];
|
||||
gets(pw);
|
||||
char *pw_ptr = pw;
|
||||
memset(pw, 0, PW_SIZE); // GOOD
|
||||
use_pw(pw_ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char pw_global[PW_SIZE];
|
||||
int funcN2() {
|
||||
gets(pw_global);
|
||||
use_pw(pw_global);
|
||||
memset(pw_global, 0, PW_SIZE); // GOOD
|
||||
return 0;
|
||||
}
|
||||
|
||||
int funcN3(unsigned long long sz) {
|
||||
struct mem m;
|
||||
gets(m.b);
|
||||
memset(&m, 0, sizeof(m)); // GOOD
|
||||
return m.a;
|
||||
}
|
||||
|
||||
void funcN(int num) {
|
||||
char pw[PW_SIZE];
|
||||
int i;
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
gets(pw);
|
||||
use_pw(pw);
|
||||
memset(pw, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
void set(int _x) {
|
||||
x = _x;
|
||||
}
|
||||
|
||||
int get()
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
void clear1() {
|
||||
memset(&x, 0, sizeof(x)); // GOOD
|
||||
}
|
||||
|
||||
void clear2() {
|
||||
memset(&(this->x), 0, sizeof(this->x)); // GOOD
|
||||
}
|
||||
|
||||
private:
|
||||
int x;
|
||||
};
|
||||
|
||||
void badFunc0_0(){
|
||||
unsigned char buff1[PW_SIZE];
|
||||
for(int i = 0; i < PW_SIZE; i++) {
|
||||
buff1[i] = 13;
|
||||
}
|
||||
memset(buff1, 0, PW_SIZE); // BAD
|
||||
}
|
||||
|
||||
void nobadFunc1_0() {
|
||||
char* buff1 = (char *) malloc(PW_SIZE);
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
}
|
||||
void badFunc1_0(){
|
||||
char * buff1 = (char *) malloc(PW_SIZE);
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
free(buff1);
|
||||
}
|
||||
void badFunc1_1(){
|
||||
unsigned char buff1[PW_SIZE];
|
||||
for(int i = 0; i < PW_SIZE; i++) {
|
||||
buff1[i] = 'a' + i;
|
||||
}
|
||||
memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
free(buff1);
|
||||
}
|
||||
void nobadFunc2_0_0(){
|
||||
unsigned char buff1[PW_SIZE];
|
||||
buff1[sizeof(buff1) - 1] = '\0';
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
printf("%s", buff1);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_1(){
|
||||
char buff1[PW_SIZE];
|
||||
gets(buff1);
|
||||
memset(buff1, '\0', sizeof(buff1));
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
printf("%s", buff1 + 3);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_2(){
|
||||
char buff1[PW_SIZE];
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
printf("%c", *buff1);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_3(char ch){
|
||||
unsigned char buff1[PW_SIZE];
|
||||
for(int i = 0; i < PW_SIZE; i++) {
|
||||
buff1[i] = ch;
|
||||
}
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
printf("%c", *(buff1 + 3));
|
||||
}
|
||||
|
||||
char * nobadFunc2_0_4(){
|
||||
char buff1[PW_SIZE];
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
return buff1;
|
||||
}
|
||||
|
||||
char * nobadFunc2_0_5(){
|
||||
char buff1[PW_SIZE];
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
|
||||
return buff1+3;
|
||||
}
|
||||
|
||||
unsigned char nobadFunc2_0_6(){
|
||||
unsigned char buff1[PW_SIZE];
|
||||
buff1[0] = 'z';
|
||||
int i;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
|
||||
return *buff1;
|
||||
}
|
||||
|
||||
unsigned char nobadFunc2_0_7(){
|
||||
char buff1[PW_SIZE];
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
|
||||
return *(buff1 + 3);
|
||||
}
|
||||
|
||||
bool nobadFunc2_1_0(unsigned char ch){
|
||||
char buff1[PW_SIZE];
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
if(*buff1 == ch) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
void nobadFunc2_1_2(){
|
||||
char buff1[PW_SIZE];
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // BAD [NOT DETECTED]
|
||||
buff1[2] = 5;
|
||||
}
|
||||
|
||||
void nobadFunc3_0(char * buffAll){
|
||||
char * buff1 = buffAll;
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc3_1(unsigned char * buffAll){
|
||||
unsigned char * buff1 = buffAll + 3;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
struct buffers
|
||||
{
|
||||
char buff1[50];
|
||||
unsigned char *buff2;
|
||||
};
|
||||
|
||||
void nobadFunc3_2(struct buffers buffAll) {
|
||||
char * buff1 = buffAll.buff1;
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc3_3(struct buffers buffAll) {
|
||||
unsigned char * buff1 = buffAll.buff2;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc3_4(struct buffers buffAll) {
|
||||
unsigned char * buff1 = buffAll.buff2 + 3;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc3_5(struct buffers * buffAll) {
|
||||
char * buff1 = buffAll->buff1;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc3_6(struct buffers *buffAll){
|
||||
unsigned char * buff1 = buffAll->buff2;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
char * globalBuff;
|
||||
|
||||
void nobadFunc4(){
|
||||
char * buff1 = globalBuff;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc4_0(){
|
||||
char * buff1 = globalBuff;
|
||||
gets(buff1);
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
void nobadFunc4_1(){
|
||||
char * buff1 = globalBuff + 3;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
buffers globalBuff1, *globalBuff2;
|
||||
|
||||
void nobadFunc4_2(){
|
||||
char * buff1 = globalBuff1.buff1;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc4_3(){
|
||||
unsigned char * buff1 = globalBuff1.buff2;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc4_4(){
|
||||
unsigned char * buff1 = globalBuff1.buff2+3;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc4_5(){
|
||||
char * buff1 = globalBuff2->buff1;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
void nobadFunc4_6(){
|
||||
unsigned char * buff1 = globalBuff2->buff2;
|
||||
memset(buff1, 0, PW_SIZE); // GOOD
|
||||
}
|
||||
|
||||
extern void use_byte(unsigned char);
|
||||
|
||||
void test_static_func() {
|
||||
static unsigned char buffer[PW_SIZE] = {0};
|
||||
use_byte(buffer[0]);
|
||||
memset(buffer, 42, sizeof(buffer)); // GOOD
|
||||
}
|
||||
Reference in New Issue
Block a user