Merge branch 'main' into java/merge-5226

This commit is contained in:
Anders Schack-Mulligen
2021-03-04 11:36:16 +01:00
312 changed files with 12208 additions and 2114 deletions

View File

@@ -0,0 +1,3 @@
char password[MAX_PASSWORD_LENGTH];
// read and verify password
memset(password, 0, MAX_PASSWORD_LENGTH);

View File

@@ -0,0 +1,3 @@
char password[MAX_PASSWORD_LENGTH];
// read and verify password
memset_s(password, MAX_PASSWORD_LENGTH, 0, MAX_PASSWORD_LENGTH);

View 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>

View 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."

View File

@@ -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)
}

View File

@@ -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>

View File

@@ -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."

View File

@@ -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() }

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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`.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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`.

View File

@@ -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" }

View File

@@ -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

View File

@@ -0,0 +1,2 @@
private import semmle.code.cpp.ir.implementation.internal.TOperand
import AliasedSSAOperands

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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" }

View File

@@ -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

View File

@@ -0,0 +1,2 @@
private import semmle.code.cpp.ir.implementation.internal.TOperand
import RawOperands

View File

@@ -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" }

View File

@@ -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

View File

@@ -0,0 +1,2 @@
private import semmle.code.cpp.ir.implementation.internal.TOperand
import UnaliasedSSAOperands

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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. |

View File

@@ -1 +0,0 @@
experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql

View File

@@ -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);
}

View File

@@ -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. |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-014/MemsetMayBeDeleted.ql

View 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;
}

View 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
}