Merge master into next.

`master` up to and including cfe0b8803a.
This commit is contained in:
Aditya Sharad
2018-10-25 15:32:47 +01:00
122 changed files with 4155 additions and 1297 deletions

View File

@@ -0,0 +1,7 @@
goto err1;
free(pointer); // BAD: this line is unreachable
err1: return -1;
free(pointer); // GOOD: this line is reachable
goto err2;
err2: return -1;

View File

@@ -0,0 +1,28 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Code immediately following a <code>goto</code> or <code>break</code> statement will not be executed,
unless there is a label or switch case. When the code is necessary, this leads to logical errors or
resource leaks. If the code is unnecessary, it may confuse readers.
</p>
</overview>
<recommendation>
<p>
If the unreachable code is necessary, move the <code>goto</code> or <code>break</code> statement to
after the code. Otherwise, delete the unreachable code.
</p>
</recommendation>
<example><sample src="DeadCodeGoto.cpp" />
</example>
<references>
<li>
The CERT C Secure Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,33 @@
/**
* @name Dead code due to goto or break statement
* @description A goto or break statement is followed by unreachable code.
* @kind problem
* @problem.severity warning
* @precision high
* @id cpp/dead-code-goto
* @tags maintainability
* external/cwe/cwe-561
*/
import cpp
Stmt getNextRealStmt(Block b, int i) {
result = b.getStmt(i + 1) and
not result instanceof EmptyStmt
or
b.getStmt(i + 1) instanceof EmptyStmt and
result = getNextRealStmt(b, i + 1)
}
from JumpStmt js, Block b, int i, Stmt s
where b.getStmt(i) = js
and s = getNextRealStmt(b, i)
// the next statement isn't jumped to
and not s instanceof LabelStmt
and not s instanceof SwitchCase
// the next statement isn't breaking out of a switch
and not s.(BreakStmt).getBreakable() instanceof SwitchStmt
// the next statement isn't a loop that can be jumped into
and not exists (LabelStmt ls | s.(Loop).getStmt().getAChild*() = ls)
and not exists (SwitchCase sc | s.(Loop).getStmt().getAChild*() = sc)
select js, "This statement makes $@ unreachable.", s, s.toString()

View File

@@ -1,13 +1,13 @@
int main(int argc, char* argv[]) {
char param[SIZE];
char param[20];
char *arg1;
char arg1[10];
char arg2[20];
arg1 = argv[1];
//wrong: only uses the size of the source (argv[1]) when using strncpy
strncpy(param, argv[1], strlen(arg1));
strncpy(param, arg1, strlen(arg1));
//correct: uses the size of the destination array as well
strncpy(param, argv[1], min(strlen(arg1, sizeof(param) -1)));
strncpy(param, arg1, min(strlen(arg1), sizeof(param) -1));
}

View File

@@ -5,26 +5,38 @@
* @kind problem
* @id cpp/overflow-destination
* @problem.severity warning
* @precision low
* @tags reliability
* security
* external/cwe/cwe-119
* external/cwe/cwe-131
*/
import cpp
import semmle.code.cpp.pointsto.PointsTo
import semmle.code.cpp.security.TaintTracking
predicate sourceSized(FunctionCall fc)
/**
* Holds if `fc` is a call to a copy operation where the size argument contains
* a reference to the source argument. For example:
* ```
* memcpy(dest, src, sizeof(src));
* ```
*/
predicate sourceSized(FunctionCall fc, Expr src)
{
exists(string name |
(name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and
fc.getTarget().hasQualifiedName(name))
and
exists(Expr dest, Expr src, Expr size, Variable v |
exists(Expr dest, Expr size, Variable v |
fc.getArgument(0) = dest and fc.getArgument(1) = src and fc.getArgument(2) = size and
src = v.getAnAccess() and size.getAChild+() = v.getAnAccess() and
// exception: `dest` is also referenced in the size argument
not exists(Variable other |
dest = other.getAnAccess() and size.getAChild+() = other.getAnAccess())
and
// exception: `src` and `dest` are both arrays of the same type and size
not exists(ArrayType srctype, ArrayType desttype |
dest.getType().getUnderlyingType() = desttype and
src.getType().getUnderlyingType() = srctype and
@@ -32,48 +44,7 @@ predicate sourceSized(FunctionCall fc)
desttype.getArraySize() = srctype.getArraySize()))
}
class VulnerableArgument extends PointsToExpr
{
VulnerableArgument() { sourceSized(this.getParent()) }
override predicate interesting() { sourceSized(this.getParent()) }
}
predicate taintingFunction(Function f, int buf)
{
(f.hasQualifiedName("read") and buf = 1) or
(f.hasQualifiedName("fgets") and buf = 0) or
(f.hasQualifiedName("fread") and buf = 0)
}
// Taint `argv[i]`, for all i, but also `*argv`, etc.
predicate commandLineArg(Expr e)
{
exists(Function f, Parameter argv, VariableAccess access |
f.hasQualifiedName("main") and f.getParameter(1) = argv and
argv.getAnAccess() = access and access.isRValue() and
pointer(access, e))
}
predicate tainted(Expr e)
{
exists(FunctionCall fc, int arg |
taintingFunction(fc.getTarget(), arg) and
e = fc.getArgument(arg))
or
e.(FunctionCall).getTarget().hasQualifiedName("getenv")
or
commandLineArg(e)
}
class TaintedArgument extends PointsToExpr
{
TaintedArgument() { tainted(this) }
override predicate interesting() { tainted(this) }
}
from FunctionCall fc, VulnerableArgument vuln, TaintedArgument tainted
where sourceSized(fc)
and fc.getArgument(1) = vuln
and vuln.pointsTo() = tainted.pointsTo()
and vuln.confidence() > 0.01
from FunctionCall fc, Expr vuln, Expr taintSource
where sourceSized(fc, vuln)
and tainted(taintSource, vuln)
select fc, "To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size."

View File

@@ -0,0 +1,7 @@
void f()
{
for (signed char i = 0; i < 100; i--)
{
// code ...
}
}

View File

@@ -0,0 +1,27 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A <code>for-loop</code> iteration expression goes backwards with respect of the initialization statement and condition expression.</p>
<p>This warning indicates that a <code>for-loop</code> might not function as intended.</p>
</overview>
<recommendation>
<p>To fix this issue, check that the loop condition is correct and change the iteration expression to match.</p>
</recommendation>
<example>
<p>In the following example, the initialization statement (<code>i = 0</code>) and the condition expression (<code>i &lt; 100</code>) indicate that the intended iteration expression should have been incrementing, but instead a postfix decrement operator is used (<code>i--</code>).</p>
<sample src="inconsistentLoopDirection.c" />
<p>To fix this issue, change the iteration expression to match the direction of the initialization statement and the condition expression: <code>i++</code>.</p>
</example>
<references>
<li><a href="https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/58teb7hd(v=vs.110)">warning C6293: Ill-defined for-loop: counts down from minimum</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,116 @@
/**
* @name Inconsistent direction of for loop
* @description A for-loop iteration expression goes backward with respect of the initialization statement and condition expression.
* @kind problem
* @problem.severity error
* @precision high
* @id cpp/inconsistent-loop-direction
* @tags correctness
* external/cwe/cwe-835
* external/microsoft/6293
* @msrc.severity important
*/
import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.dataflow.DataFlow
predicate illDefinedDecrForStmt( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition ) {
v.getAnAssignedValue() = initialCondition
and
exists(
RelationalOperation rel |
rel = forstmt.getCondition() |
terminalCondition = rel.getGreaterOperand()
and v.getAnAccess() = rel.getLesserOperand()
and
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(rel.getLesserOperand()))
)
and
exists(
DecrementOperation dec |
dec = forstmt.getUpdate().(DecrementOperation)
and dec.getAnOperand() = v.getAnAccess()
)
and
(
( upperBound(initialCondition) < lowerBound(terminalCondition) )
or
( forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue() )
)
}
predicate illDefinedIncrForStmt( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition ) {
v.getAnAssignedValue() = initialCondition
and
exists(
RelationalOperation rel |
rel = forstmt.getCondition() |
terminalCondition = rel.getLesserOperand()
and v.getAnAccess() = rel.getGreaterOperand()
and
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(rel.getGreaterOperand()))
)
and
exists( IncrementOperation incr |
incr = forstmt.getUpdate().(IncrementOperation)
and
incr.getAnOperand() = v.getAnAccess()
)
and
(
( upperBound(terminalCondition) < lowerBound(initialCondition))
or
( forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
)
}
predicate illDefinedForStmtWrongDirection( ForStmt forstmt, Variable v, Expr initialCondition, Expr terminalCondition
, boolean isIncr ) {
( illDefinedDecrForStmt( forstmt, v, initialCondition, terminalCondition) and isIncr = false )
or
( illDefinedIncrForStmt( forstmt, v, initialCondition, terminalCondition) and isIncr = true)
}
bindingset[b]
private string forLoopdirection(boolean b){
if( b = true ) then result = "upward"
else result = "downward"
}
bindingset[b]
private string forLoopTerminalConditionRelationship(boolean b){
if( b = true ) then result = "lower"
else result = "higher"
}
predicate illDefinedForStmt( ForStmt for, string message ) {
exists(
boolean isIncr,
Variable v,
Expr initialCondition,
Expr terminalCondition |
illDefinedForStmtWrongDirection(for, v, initialCondition, terminalCondition, isIncr)
and
if( for.conditionAlwaysFalse() ) then
(
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts "
+ forLoopdirection(isIncr) + " from a value ("+ initialCondition +"), but the terminal condition is always false."
)
else if
(
for.conditionAlwaysTrue() ) then (
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts "
+ forLoopdirection(isIncr) + " from a value ("+ initialCondition +"), but the terminal condition is always true."
)
else
(
message = "Ill-defined for-loop: a loop using variable \"" + v + "\" counts "
+ forLoopdirection(isIncr) + " from a value ("+ initialCondition +"), but the terminal condition is "
+ forLoopTerminalConditionRelationship(isIncr) + " (" + terminalCondition + ")."
)
)
}
from ForStmt forstmt, string message
where illDefinedForStmt(forstmt, message)
select forstmt, message

View File

@@ -0,0 +1,11 @@
STARTUPINFOW si;
PROCESS_INFORMATION pi;
// ...
CreateProcessW( // BUG
NULL, // lpApplicationName
(LPWSTR)L"C:\\Program Files\\MyApp", // lpCommandLine
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
// ...

View File

@@ -0,0 +1,46 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query indicates that there is a call to a function of the <code>CreateProcess*</code> family of functions, which introduces a security vulnerability.</p>
</overview>
<recommendation>
<p>Do not use <code>NULL</code> for the <code>lpApplicationName</code> argument to the <code>CreateProcess*</code> function.</p>
<p>If you pass <code>NULL</code> for <code>lpApplicationName</code>, use quotation marks around the executable path in <code>lpCommandLine</code>.</p>
</recommendation>
<example>
<p>In the following example, <code>CreateProcessW</code> is called with a <code>NULL</code> value for <code>lpApplicationName</code>,
and the value for <code>lpCommandLine</code> that represent the application path is not quoted and has spaces in it.</p>
<p>If an attacker has access to the file system, they can elevate privileges by creating a file such as <code>C:\Program.exe</code> that will be executed instead of the intended application.</p>
<sample src="UnsafeCreateProcessCall.cpp" />
<p>To fix this issue, specify a valid string for <code>lpApplicationName</code>, or quote the path for <code>lpCommandLine</code>. For example:</p>
<p><code>(LPWSTR)L"\"C:\\Program Files\\MyApp\"", // lpCommandLine</code></p>
</example>
<references>
<li>
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessa">CreateProcessA function (Microsoft documentation).</a>
</li>
<li>
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessw">CreateProcessW function (Microsoft documentation).</a>
</li>
<li>
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasusera">CreateProcessAsUserA function (Microsoft documentation).</a>
</li>
<li>
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createprocessasuserw">CreateProcessAsUserW function (Microsoft documentation).</a>
</li>
<li>
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithlogonw">CreateProcessWithLogonW function (Microsoft documentation).</a>
</li>
<li>
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createprocesswithtokenw">CreateProcessWithTokenW function (Microsoft documentation).</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,130 @@
/**
* @name NULL application name with an unquoted path in call to CreateProcess
* @description Calling a function of the CreateProcess* family of functions, where the path contains spaces, introduces a security vulnerability.
* @id cpp/unsafe-create-process-call
* @kind problem
* @problem.severity error
* @precision medium
* @msrc.severity important
* @tags security
* external/cwe/cwe-428
* external/microsoft/C6277
*/
import cpp
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.dataflow.DataFlow2
predicate isCreateProcessFunction(FunctionCall call, int applicationNameIndex, int commandLineIndex) {
(
call.getTarget().hasGlobalName("CreateProcessA")
and applicationNameIndex = 0
and commandLineIndex = 1
) or (
call.getTarget().hasGlobalName("CreateProcessW")
and applicationNameIndex = 0
and commandLineIndex = 1
) or (
call.getTarget().hasGlobalName("CreateProcessWithTokenW")
and applicationNameIndex = 2
and commandLineIndex = 3
) or (
call.getTarget().hasGlobalName("CreateProcessWithLogonW")
and applicationNameIndex = 4
and commandLineIndex = 5
) or (
call.getTarget().hasGlobalName("CreateProcessAsUserA")
and applicationNameIndex = 1
and commandLineIndex = 2
) or (
call.getTarget().hasGlobalName("CreateProcessAsUserW")
and applicationNameIndex = 1
and commandLineIndex = 2
)
}
/**
* A function call to CreateProcess (either wide-char or single byte string versions)
*/
class CreateProcessFunctionCall extends FunctionCall {
CreateProcessFunctionCall() {
isCreateProcessFunction( this, _, _)
}
int getApplicationNameArgumentId() {
isCreateProcessFunction( this, result, _)
}
int getCommandLineArgumentId() {
isCreateProcessFunction( this, _, result)
}
}
/**
* Dataflow that detects a call to CreateProcess with a NULL value for lpApplicationName argument
*/
class NullAppNameCreateProcessFunctionConfiguration extends DataFlow::Configuration {
NullAppNameCreateProcessFunctionConfiguration() {
this = "NullAppNameCreateProcessFunctionConfiguration"
}
override predicate isSource(DataFlow::Node source) {
nullValue(source.asExpr())
}
override predicate isSink(DataFlow::Node sink) {
exists(
CreateProcessFunctionCall call, Expr val |
val = sink.asExpr() |
val = call.getArgument(call.getApplicationNameArgumentId())
)
}
}
/**
* Dataflow that detects a call to CreateProcess with an unquoted commandLine argument
*/
class QuotedCommandInCreateProcessFunctionConfiguration extends DataFlow2::Configuration {
QuotedCommandInCreateProcessFunctionConfiguration() {
this = "QuotedCommandInCreateProcessFunctionConfiguration"
}
override predicate isSource(DataFlow2::Node source) {
exists( string s |
s = source.asExpr().getValue().toString()
and
not isQuotedOrNoSpaceApplicationNameOnCmd(s)
)
}
override predicate isSink(DataFlow2::Node sink) {
exists(
CreateProcessFunctionCall call, Expr val |
val = sink.asExpr() |
val = call.getArgument(call.getCommandLineArgumentId())
)
}
}
bindingset[s]
predicate isQuotedOrNoSpaceApplicationNameOnCmd(string s){
s.regexpMatch("\"([^\"])*\"(\\s|.)*") // The first element (path) is quoted
or
s.regexpMatch("[^\\s]+") // There are no spaces in the string
}
from CreateProcessFunctionCall call, string msg1, string msg2
where
exists( Expr source, Expr appName,
NullAppNameCreateProcessFunctionConfiguration nullAppConfig |
appName = call.getArgument(call.getApplicationNameArgumentId())
and nullAppConfig.hasFlow(DataFlow2::exprNode(source), DataFlow2::exprNode(appName))
and msg1 = call.toString() + " with lpApplicationName == NULL (" + appName + ")"
)
and
exists( Expr source, Expr cmd,
QuotedCommandInCreateProcessFunctionConfiguration quotedConfig |
cmd = call.getArgument(call.getCommandLineArgumentId())
and quotedConfig.hasFlow(DataFlow2::exprNode(source), DataFlow2::exprNode(cmd))
and msg2 = " and with an unquoted lpCommandLine (" + cmd + ") introduces a security vulnerability if the path contains spaces."
)
select call, msg1 + " " + msg2

View File

@@ -107,15 +107,17 @@ predicate defUndef(File f, string macroName) {
}
/**
* Header file `hf` looks like it contains an x-macro called
* `macroName`. That is, a macro that is used to interpret the
* Header file `hf` looks like it contains an x-macro.
* That is, a macro that is used to interpret the
* data in `hf`, usually defined just before including that file
* and undefined immediately afterwards.
*/
predicate hasXMacro(HeaderFile hf, string macroName) {
usesMacro(hf, macroName) and
forex(Include i | i.getIncludedFile() = hf |
defUndef(i.getFile(), macroName)
predicate hasXMacro(HeaderFile hf) {
exists(string macroName |
usesMacro(hf, macroName) and
forex(File f | f.getAnIncludedFile() = hf |
defUndef(f, macroName)
)
)
}
@@ -135,7 +137,7 @@ where not hf instanceof IncludeGuardedHeader
exists(UsingEntry ue | ue.getFile() = hf)
)
// Exclude files which look like they contain 'x-macros'
and not hasXMacro(hf, _)
and not hasXMacro(hf)
// Exclude files which are always #imported.
and not forex(Include i | i.getIncludedFile() = hf | i instanceof Import)
// Exclude files which are only included once.

View File

@@ -159,6 +159,17 @@ predicate unreleasedResource(Resource r, Expr acquire, File f, int acquireLine)
)
and f = acquire.getFile()
and acquireLine = acquire.getLocation().getStartLine()
// check that any destructor for this class has a block; if it doesn't,
// we must be missing information.
and forall(Class c, Destructor d |
r.getDeclaringType().isConstructedFrom*(c) and
d = c.getAMember() and
not d.isCompilerGenerated() and
not d.isDefaulted() and
not d.isDeleted() |
exists(d.getBlock())
)
}
predicate freedInSameMethod(Resource r, Expr acquire) {

View File

@@ -19,7 +19,11 @@ import cpp
predicate functionsMissingReturnStmt(Function f, ControlFlowNode blame) {
f.fromSource() and
not f.getType().getUnderlyingType().getUnspecifiedType() instanceof VoidType and
exists(Type returnType |
returnType = f.getType().getUnderlyingType().getUnspecifiedType() and
not returnType instanceof VoidType and
not returnType instanceof TemplateParameter
) and
exists(ReturnStmt s | f.getAPredecessor() = s | blame = s.getAPredecessor())}
/* If a function has a value-carrying return statement, but the extractor hit a snag
@@ -32,13 +36,11 @@ predicate functionImperfectlyExtracted(Function f) {
exists(ErrorExpr ee | ee.getEnclosingFunction() = f)
}
from Stmt stmt, string msg
from Stmt stmt, string msg, Function f, ControlFlowNode blame
where
exists(Function f, ControlFlowNode blame |
functionsMissingReturnStmt(f, blame) and
reachable(blame) and
not functionImperfectlyExtracted(f) and
(blame = stmt or blame.(Expr).getEnclosingStmt() = stmt) and
msg = "Function " + f.getName() + " should return a value of type " + f.getType().getName() + " but does not return a value here"
)
functionsMissingReturnStmt(f, blame) and
reachable(blame) and
not functionImperfectlyExtracted(f) and
(blame = stmt or blame.(Expr).getEnclosingStmt() = stmt) and
msg = "Function " + f.getName() + " should return a value of type " + f.getType().getName() + " but does not return a value here"
select stmt, msg

View File

@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
localFlowExit(node2, config)
}
/**
* Holds if `f` may contain an object of the same type, `t`, as the one
* that contains `f`, and if this fact should be used to compress
* access paths.
*
* Examples include the tail pointer in a linked list or the left and right
* pointers in a binary tree.
*/
private predicate selfRef(Content f, RefType t) {
t = f.getDeclaringType() and
f.isSelfRef()
}
private newtype TFlowContainer =
TRegularContent(Content f) { not selfRef(f, _) } or
TSelfRefContent(RefType t) { selfRef(_, t) }
/**
* A `Content` or a `Content` abstracted as its declaring type.
*
* Sequences of one or more `Content`s in the same declaring type for which
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
* `AccessPath`.
*/
private class FlowContainer extends TFlowContainer {
string toString() {
exists(Content f | this = TRegularContent(f) and result = f.toString())
or
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
}
predicate usesContent(Content f) {
this = TRegularContent(f)
or
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
}
RefType getContainerType() {
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
or
this = TSelfRefContent(result)
}
}
private newtype TAccessPathFront =
TFrontNil(Type t) or
TFrontHead(FlowContainer f)
TFrontHead(Content f)
/**
* The front of an `AccessPath`. This is either a head or a nil.
*/
private class AccessPathFront extends TAccessPathFront {
string toString() {
exists(Type t | this = TFrontNil(t) | result = t.toString())
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
or
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
exists(Content f | this = TFrontHead(f) | result = f.toString())
}
Type getType() {
this = TFrontNil(result)
or
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
}
predicate headUsesContent(Content f) {
exists(FlowContainer fc |
fc.usesContent(f) and
this = TFrontHead(fc)
)
}
predicate headUsesContent(Content f) { this = TFrontHead(f) }
}
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
private newtype TAccessPath =
TNil(Type t) or
TCons(FlowContainer f, int len) { len in [1 .. 5] }
TCons(Content f, int len) { len in [1 .. 5] }
/**
* Conceptually a list of `Content`s followed by a `Type`, but only the first
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
private class AccessPath extends TAccessPath {
abstract string toString();
FlowContainer getHead() { this = TCons(result, _) }
Content getHead() { this = TCons(result, _) }
int len() {
this = TNil(_) and result = 0
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
Type getType() {
this = TNil(result)
or
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class AccessPathNil extends AccessPath, TNil {
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
}
private class AccessPathCons extends AccessPath, TCons {
override string toString() {
exists(FlowContainer f, int len | this = TCons(f, len) |
exists(Content f, int len | this = TCons(f, len) |
result = f.toString() + ", ... (" + len.toString() + ")"
)
}
override AccessPathFront getFront() {
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
}
}

View File

@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
localFlowExit(node2, config)
}
/**
* Holds if `f` may contain an object of the same type, `t`, as the one
* that contains `f`, and if this fact should be used to compress
* access paths.
*
* Examples include the tail pointer in a linked list or the left and right
* pointers in a binary tree.
*/
private predicate selfRef(Content f, RefType t) {
t = f.getDeclaringType() and
f.isSelfRef()
}
private newtype TFlowContainer =
TRegularContent(Content f) { not selfRef(f, _) } or
TSelfRefContent(RefType t) { selfRef(_, t) }
/**
* A `Content` or a `Content` abstracted as its declaring type.
*
* Sequences of one or more `Content`s in the same declaring type for which
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
* `AccessPath`.
*/
private class FlowContainer extends TFlowContainer {
string toString() {
exists(Content f | this = TRegularContent(f) and result = f.toString())
or
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
}
predicate usesContent(Content f) {
this = TRegularContent(f)
or
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
}
RefType getContainerType() {
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
or
this = TSelfRefContent(result)
}
}
private newtype TAccessPathFront =
TFrontNil(Type t) or
TFrontHead(FlowContainer f)
TFrontHead(Content f)
/**
* The front of an `AccessPath`. This is either a head or a nil.
*/
private class AccessPathFront extends TAccessPathFront {
string toString() {
exists(Type t | this = TFrontNil(t) | result = t.toString())
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
or
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
exists(Content f | this = TFrontHead(f) | result = f.toString())
}
Type getType() {
this = TFrontNil(result)
or
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
}
predicate headUsesContent(Content f) {
exists(FlowContainer fc |
fc.usesContent(f) and
this = TFrontHead(fc)
)
}
predicate headUsesContent(Content f) { this = TFrontHead(f) }
}
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
private newtype TAccessPath =
TNil(Type t) or
TCons(FlowContainer f, int len) { len in [1 .. 5] }
TCons(Content f, int len) { len in [1 .. 5] }
/**
* Conceptually a list of `Content`s followed by a `Type`, but only the first
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
private class AccessPath extends TAccessPath {
abstract string toString();
FlowContainer getHead() { this = TCons(result, _) }
Content getHead() { this = TCons(result, _) }
int len() {
this = TNil(_) and result = 0
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
Type getType() {
this = TNil(result)
or
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class AccessPathNil extends AccessPath, TNil {
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
}
private class AccessPathCons extends AccessPath, TCons {
override string toString() {
exists(FlowContainer f, int len | this = TCons(f, len) |
exists(Content f, int len | this = TCons(f, len) |
result = f.toString() + ", ... (" + len.toString() + ")"
)
}
override AccessPathFront getFront() {
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
}
}

View File

@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
localFlowExit(node2, config)
}
/**
* Holds if `f` may contain an object of the same type, `t`, as the one
* that contains `f`, and if this fact should be used to compress
* access paths.
*
* Examples include the tail pointer in a linked list or the left and right
* pointers in a binary tree.
*/
private predicate selfRef(Content f, RefType t) {
t = f.getDeclaringType() and
f.isSelfRef()
}
private newtype TFlowContainer =
TRegularContent(Content f) { not selfRef(f, _) } or
TSelfRefContent(RefType t) { selfRef(_, t) }
/**
* A `Content` or a `Content` abstracted as its declaring type.
*
* Sequences of one or more `Content`s in the same declaring type for which
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
* `AccessPath`.
*/
private class FlowContainer extends TFlowContainer {
string toString() {
exists(Content f | this = TRegularContent(f) and result = f.toString())
or
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
}
predicate usesContent(Content f) {
this = TRegularContent(f)
or
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
}
RefType getContainerType() {
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
or
this = TSelfRefContent(result)
}
}
private newtype TAccessPathFront =
TFrontNil(Type t) or
TFrontHead(FlowContainer f)
TFrontHead(Content f)
/**
* The front of an `AccessPath`. This is either a head or a nil.
*/
private class AccessPathFront extends TAccessPathFront {
string toString() {
exists(Type t | this = TFrontNil(t) | result = t.toString())
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
or
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
exists(Content f | this = TFrontHead(f) | result = f.toString())
}
Type getType() {
this = TFrontNil(result)
or
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
}
predicate headUsesContent(Content f) {
exists(FlowContainer fc |
fc.usesContent(f) and
this = TFrontHead(fc)
)
}
predicate headUsesContent(Content f) { this = TFrontHead(f) }
}
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
private newtype TAccessPath =
TNil(Type t) or
TCons(FlowContainer f, int len) { len in [1 .. 5] }
TCons(Content f, int len) { len in [1 .. 5] }
/**
* Conceptually a list of `Content`s followed by a `Type`, but only the first
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
private class AccessPath extends TAccessPath {
abstract string toString();
FlowContainer getHead() { this = TCons(result, _) }
Content getHead() { this = TCons(result, _) }
int len() {
this = TNil(_) and result = 0
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
Type getType() {
this = TNil(result)
or
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class AccessPathNil extends AccessPath, TNil {
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
}
private class AccessPathCons extends AccessPath, TCons {
override string toString() {
exists(FlowContainer f, int len | this = TCons(f, len) |
exists(Content f, int len | this = TCons(f, len) |
result = f.toString() + ", ... (" + len.toString() + ")"
)
}
override AccessPathFront getFront() {
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
}
}

View File

@@ -724,76 +724,27 @@ private predicate localFlowBigStep(
localFlowExit(node2, config)
}
/**
* Holds if `f` may contain an object of the same type, `t`, as the one
* that contains `f`, and if this fact should be used to compress
* access paths.
*
* Examples include the tail pointer in a linked list or the left and right
* pointers in a binary tree.
*/
private predicate selfRef(Content f, RefType t) {
t = f.getDeclaringType() and
f.isSelfRef()
}
private newtype TFlowContainer =
TRegularContent(Content f) { not selfRef(f, _) } or
TSelfRefContent(RefType t) { selfRef(_, t) }
/**
* A `Content` or a `Content` abstracted as its declaring type.
*
* Sequences of one or more `Content`s in the same declaring type for which
* `isSelfRef()` holds are represented as a single `FlowContainer` in an
* `AccessPath`.
*/
private class FlowContainer extends TFlowContainer {
string toString() {
exists(Content f | this = TRegularContent(f) and result = f.toString())
or
exists(RefType t | this = TSelfRefContent(t) and result = t.toString())
}
predicate usesContent(Content f) {
this = TRegularContent(f)
or
exists(RefType t | this = TSelfRefContent(t) and selfRef(f, t))
}
RefType getContainerType() {
exists(Content f | this = TRegularContent(f) and result = f.getDeclaringType())
or
this = TSelfRefContent(result)
}
}
private newtype TAccessPathFront =
TFrontNil(Type t) or
TFrontHead(FlowContainer f)
TFrontHead(Content f)
/**
* The front of an `AccessPath`. This is either a head or a nil.
*/
private class AccessPathFront extends TAccessPathFront {
string toString() {
exists(Type t | this = TFrontNil(t) | result = t.toString())
exists(Type t | this = TFrontNil(t) | result = ppReprType(t))
or
exists(FlowContainer f | this = TFrontHead(f) | result = f.toString())
exists(Content f | this = TFrontHead(f) | result = f.toString())
}
Type getType() {
this = TFrontNil(result)
or
exists(FlowContainer head | this = TFrontHead(head) | result = head.getContainerType())
exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
}
predicate headUsesContent(Content f) {
exists(FlowContainer fc |
fc.usesContent(f) and
this = TFrontHead(fc)
)
}
predicate headUsesContent(Content f) { this = TFrontHead(f) }
}
private class AccessPathFrontNil extends AccessPathFront, TFrontNil { }
@@ -1004,7 +955,7 @@ private predicate consCand(Content f, AccessPathFront apf, Configuration config)
private newtype TAccessPath =
TNil(Type t) or
TCons(FlowContainer f, int len) { len in [1 .. 5] }
TCons(Content f, int len) { len in [1 .. 5] }
/**
* Conceptually a list of `Content`s followed by a `Type`, but only the first
@@ -1016,7 +967,7 @@ private newtype TAccessPath =
private class AccessPath extends TAccessPath {
abstract string toString();
FlowContainer getHead() { this = TCons(result, _) }
Content getHead() { this = TCons(result, _) }
int len() {
this = TNil(_) and result = 0
@@ -1027,27 +978,27 @@ private class AccessPath extends TAccessPath {
Type getType() {
this = TNil(result)
or
exists(FlowContainer head | this = TCons(head, _) | result = head.getContainerType())
exists(Content head | this = TCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class AccessPathNil extends AccessPath, TNil {
override string toString() { exists(Type t | this = TNil(t) | result = t.toString()) }
override string toString() { exists(Type t | this = TNil(t) | result = ppReprType(t)) }
override AccessPathFront getFront() { exists(Type t | this = TNil(t) | result = TFrontNil(t)) }
}
private class AccessPathCons extends AccessPath, TCons {
override string toString() {
exists(FlowContainer f, int len | this = TCons(f, len) |
exists(Content f, int len | this = TCons(f, len) |
result = f.toString() + ", ... (" + len.toString() + ")"
)
}
override AccessPathFront getFront() {
exists(FlowContainer f | this = TCons(f, _) | result = TFrontHead(f))
exists(Content f | this = TCons(f, _) | result = TFrontHead(f))
}
}

View File

@@ -118,7 +118,7 @@ private module ImplCommon {
node1.(ArgumentNode).argumentOf(call, i1) and
node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
compatibleTypes(node1.getTypeBound(), f.getType()) and
compatibleTypes(node2.getTypeBound(), f.getDeclaringType())
compatibleTypes(node2.getTypeBound(), f.getContainerType())
)
}
@@ -149,7 +149,7 @@ private module ImplCommon {
setterReturn(p, f) and
arg.argumentOf(node2.asExpr(), _) and
compatibleTypes(node1.getTypeBound(), f.getType()) and
compatibleTypes(node2.getTypeBound(), f.getDeclaringType())
compatibleTypes(node2.getTypeBound(), f.getContainerType())
)
}
@@ -174,7 +174,7 @@ private module ImplCommon {
viableParamArg(p, arg) and
getter(p, f) and
arg.argumentOf(node2.asExpr(), _) and
compatibleTypes(node1.getTypeBound(), f.getDeclaringType()) and
compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
compatibleTypes(node2.getTypeBound(), f.getType())
)
}

View File

@@ -69,18 +69,9 @@ class Content extends TContent {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
/** Gets the type of the object containing this content. */
abstract RefType getDeclaringType();
abstract RefType getContainerType();
/** Gets the type of this content. */
abstract Type getType();
/**
* Holds if this content may contain an object of the same type as the one
* that contains this content, and if this fact should be used to compress
* access paths.
*
* Examples include the tail pointer in a linked list or the left and right
* pointers in a binary tree.
*/
predicate isSelfRef() { none() }
}
private class FieldContent extends Content, TFieldContent {
Field f;
@@ -90,17 +81,17 @@ private class FieldContent extends Content, TFieldContent {
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
override RefType getDeclaringType() { result = f.getDeclaringType() }
override RefType getContainerType() { result = f.getDeclaringType() }
override Type getType() { result = f.getType() }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
override RefType getDeclaringType() { none() }
override RefType getContainerType() { none() }
override Type getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "array" }
override RefType getDeclaringType() { none() }
override RefType getContainerType() { none() }
override Type getType() { none() }
}
@@ -132,6 +123,11 @@ RefType getErasedRepr(Type t) {
result instanceof VoidType // stub implementation
}
/** Gets a string representation of a type returned by `getErasedRepr`. */
string ppReprType(Type t) {
result = t.toString()
}
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.

View File

@@ -114,8 +114,11 @@ pragma[noopt] predicate correctIncludeGuard(HeaderFile hf, PreprocessorDirective
*/
predicate startsWithIfndef(HeaderFile hf, PreprocessorDirective ifndef, string macroName) {
ifndefDirective(ifndef, macroName) and
ifndef.getFile() = hf and
ifndef.getLocation().getStartLine() = min(int l | includeGuardRelevantLine(hf, l))
exists(Location loc |
loc = ifndef.getLocation() and
loc.getFile() = hf and
loc.getStartLine() = min(int l | includeGuardRelevantLine(hf, l))
)
}
private predicate endifLocation(PreprocessorEndif endif, File f, int line) {