Merge remote-tracking branch 'upstream/master' into dbartol/MissingToString

This commit is contained in:
Dave Bartolomeo
2019-12-17 11:50:36 -07:00
35 changed files with 402 additions and 133 deletions

View File

@@ -3,7 +3,7 @@
* @description A function that uses more functions and variables from another file than functions and variables from its own file. This function might be better placed in the other file, to avoid exposing internals of the file it depends on.
* @kind problem
* @problem.severity recommendation
* @precision high
* @precision medium
* @id cpp/feature-envy
* @tags maintainability
* modularity

View File

@@ -3,7 +3,7 @@
* @description Two files share too much information about each other (accessing many operations or variables in both directions). It would be better to invert some of the dependencies to reduce the coupling between the two files.
* @kind problem
* @problem.severity recommendation
* @precision high
* @precision medium
* @id cpp/file-intimacy
* @tags maintainability
* modularity

View File

@@ -3,7 +3,7 @@
* @description Finds classes with many fields; they could probably be refactored by breaking them down into smaller classes, and using composition.
* @kind problem
* @problem.severity recommendation
* @precision high
* @precision medium
* @id cpp/class-many-fields
* @tags maintainability
* statistical

View File

@@ -4,7 +4,7 @@
* @kind problem
* @problem.severity warning
* @id cpp/japanese-era/exact-era-date
* @precision medium
* @precision low
* @tags reliability
* japanese-era
*/

View File

@@ -31,6 +31,12 @@ abstract class XMLLocatable extends @xmllocatable {
* both of which can contain other elements.
*/
class XMLParent extends @xmlparent {
XMLParent() {
// explicitly restrict `this` to be either an `XMLElement` or an `XMLFile`;
// the type `@xmlparent` currently also includes non-XML files
this instanceof @xmlelement or xmlEncoding(this, _)
}
/**
* Gets a printable representation of this XML parent.
* (Intended to be overridden in subclasses.)

View File

@@ -51,7 +51,11 @@ predicate allocationFunction(Function f) {
name = "HeapReAlloc" or
name = "VirtualAlloc" or
name = "CoTaskMemAlloc" or
name = "CoTaskMemRealloc"
name = "CoTaskMemRealloc" or
name = "kmem_alloc" or
name = "kmem_zalloc" or
name = "pool_get" or
name = "pool_cache_get"
)
)
}
@@ -77,6 +81,12 @@ predicate freeFunction(Function f, int argNum) {
name = "free" and argNum = 0
or
name = "realloc" and argNum = 0
or
name = "kmem_free" and argNum = 0
or
name = "pool_put" and argNum = 1
or
name = "pool_cache_put" and argNum = 1
)
or
f.hasGlobalOrStdName(name) and

View File

@@ -1,6 +1,7 @@
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.DataFlow2
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
@@ -29,20 +30,60 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
}
override predicate isBarrier(DataFlow::Node node) {
exists(Variable checkedVar |
accessesVariable(node.asInstruction(), checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
}
private predicate accessesVariable(CopyInstruction copy, Variable var) {
exists(VariableAddressInstruction va | va.getASTVariable() = var |
copy.(StoreInstruction).getDestinationAddress() = va
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) }
override predicate isSink(DataFlow::Node sink) {
exists(GlobalOrNamespaceVariable gv | writesVariable(sink.asInstruction(), gv))
}
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
or
copy.(LoadInstruction).getSourceAddress() = va
)
exists(StoreInstruction i1, LoadInstruction i2, GlobalOrNamespaceVariable gv |
writesVariable(i1, gv) and
readsVariable(i2, gv) and
i1 = n1.asInstruction() and
i2 = n2.asInstruction()
)
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
}
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) {
exists(
ToGlobalVarTaintTrackingCfg other, DataFlow::Node prevSink, GlobalOrNamespaceVariable gv
|
other.hasFlowTo(prevSink) and
writesVariable(prevSink.asInstruction(), gv) and
readsVariable(source.asInstruction(), gv)
)
}
override predicate isSink(DataFlow::Node sink) { any() }
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
}
private predicate writesVariable(StoreInstruction store, Variable var) {
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
}
/**
@@ -62,6 +103,13 @@ private predicate hasUpperBoundsCheck(Variable var) {
)
}
private predicate nodeIsBarrier(DataFlow::Node node) {
exists(Variable checkedVar |
readsVariable(node.asInstruction(), checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
}
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted
i2 = any(CallInstruction call |
@@ -99,51 +147,73 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// addition expression.
}
private Element adjustedSink(DataFlow::Node sink) {
// TODO: is it more appropriate to use asConvertedExpr here and avoid
// `getConversion*`? Or will that cause us to miss some cases where there's
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
// pretend there was flow to the converted `Expr` for the sake of
// compatibility.
sink.asExpr().getConversion*() = result
or
// For compatibility, send flow from arguments to parameters, even for
// functions with no body.
exists(FunctionCall call, int i |
sink.asExpr() = call.getArgument(i) and
result = resolveCall(call).getParameter(i)
)
or
// For compatibility, send flow into a `Variable` if there is flow to any
// Load or Store of that variable.
exists(CopyInstruction copy |
copy.getSourceValue() = sink.asInstruction() and
(
readsVariable(copy, result) or
writesVariable(copy, result)
) and
not hasUpperBoundsCheck(result)
)
or
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
result.(NotExpr).getOperand() = sink.asExpr()
}
predicate tainted(Expr source, Element tainted) {
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
cfg.hasFlow(DataFlow::exprNode(source), sink)
|
// TODO: is it more appropriate to use asConvertedExpr here and avoid
// `getConversion*`? Or will that cause us to miss some cases where there's
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
// pretend there was flow to the converted `Expr` for the sake of
// compatibility.
sink.asExpr().getConversion*() = tainted
or
// For compatibility, send flow from arguments to parameters, even for
// functions with no body.
exists(FunctionCall call, int i |
sink.asExpr() = call.getArgument(i) and
tainted = resolveCall(call).getParameter(i)
)
or
// For compatibility, send flow into a `Variable` if there is flow to any
// Load or Store of that variable.
exists(CopyInstruction copy |
copy.getSourceValue() = sink.asInstruction() and
accessesVariable(copy, tainted) and
not hasUpperBoundsCheck(tainted)
)
or
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
tainted.(NotExpr).getOperand() = sink.asExpr()
tainted = adjustedSink(sink)
)
}
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and
// TODO: Find a way to emulate how `security.TaintTracking` reports the last
// global variable that taint has passed through. Also make sure we emulate
// its behavior for interprocedural flow through globals.
globalVar = ""
or
exists(
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg, DataFlow::Node store,
GlobalOrNamespaceVariable global, DataFlow::Node load, DataFlow::Node sink
|
toCfg.hasFlow(DataFlow::exprNode(source), store) and
store
.asInstruction()
.(StoreInstruction)
.getDestinationAddress()
.(VariableAddressInstruction)
.getASTVariable() = global and
load
.asInstruction()
.(LoadInstruction)
.getSourceAddress()
.(VariableAddressInstruction)
.getASTVariable() = global and
fromCfg.hasFlow(load, sink) and
tainted = adjustedSink(sink) and
global = globalVarFromId(globalVar)
)
}
GlobalOrNamespaceVariable globalVarFromId(string id) {
// TODO: Implement this when `taintedIncludingGlobalVars` has support for
// global variables.
none()
}
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
Function resolveCall(Call call) {
exists(CallInstruction callInstruction |

View File

@@ -1,4 +0,0 @@
| c.c | library-tests/files/c.c |
| files1.cpp | library-tests/files/files1.cpp |
| files1.h | library-tests/files/files1.h |
| files2.cpp | library-tests/files/files2.cpp |

View File

@@ -1,5 +0,0 @@
import cpp
from File f
where f.toString() != ""
select f.toString(), f.getRelativePath()

View File

@@ -0,0 +1,4 @@
| c.c | library-tests/files/c.c | CFile, MetricFile | C | | |
| files1.cpp | library-tests/files/files1.cpp | CppFile, MetricFile | C++ | swap | t |
| files1.h | library-tests/files/files1.h | HeaderFile, MetricFile | | swap | |
| files2.cpp | library-tests/files/files2.cpp | CppFile, MetricFile | C++ | g | x, y |

View File

@@ -0,0 +1,18 @@
import cpp
string describe(File f) {
f.compiledAsC() and
result = "C"
or
f.compiledAsCpp() and
result = "C++"
or
f instanceof XMLParent and
result = "XMLParent" // regression tests a bug in the characteristic predicate of XMLParent
}
from File f
where f.toString() != ""
select f.toString(), f.getRelativePath(), concat(f.getAQlClass().toString(), ", "),
concat(describe(f), ", "), concat(f.getATopLevelDeclaration().toString(), ", "),
concat(LocalVariable v | f.getADeclaration() = v | v.toString(), ", ")

View File

@@ -1 +0,0 @@
| files1.h:0:0:0:0 | files1.h | files1.cpp:4:6:4:9 | swap |

View File

@@ -1,4 +0,0 @@
import cpp
from HeaderFile f
select f, f.getATopLevelDeclaration()

View File

@@ -1,8 +0,0 @@
| CFile | C | --- | c.c |
| CppFile | - | C++ | files1.cpp |
| CppFile | - | C++ | files2.cpp |
| HeaderFile | - | --- | files1.h |
| MetricFile | - | --- | files1.h |
| MetricFile | - | C++ | files1.cpp |
| MetricFile | - | C++ | files2.cpp |
| MetricFile | C | --- | c.c |

View File

@@ -1,11 +0,0 @@
import cpp
string isCompiledAsC(File f) { if f.compiledAsC() then result = "C" else result = "-" }
string isCompiledAsCpp(File f) { if f.compiledAsCpp() then result = "C++" else result = "---" }
from File f
// On 64bit Linux, __va_list_tag is in the unknown file (""). Ignore it.
where f.getAbsolutePath() != ""
select (f.getAQlClass().toString() + " ").prefix(10), isCompiledAsC(f), isCompiledAsCpp(f),
f.toString()

View File

@@ -1,3 +0,0 @@
| files1.cpp | files1.cpp:4:6:4:9 | swap |
| files1.h | files1.cpp:4:6:4:9 | swap |
| files2.cpp | files2.cpp:3:6:3:6 | g |

View File

@@ -1,7 +0,0 @@
import cpp
from File f, Declaration d
where
d = f.getATopLevelDeclaration() and
d.getName() != "__va_list_tag"
select f.toString(), d

View File

@@ -1,3 +0,0 @@
| files1.cpp | files1.cpp:6:6:6:6 | t |
| files2.cpp | files2.cpp:4:6:4:6 | x |
| files2.cpp | files2.cpp:5:6:5:6 | y |

View File

@@ -1,5 +0,0 @@
import cpp
from File f, LocalVariable v
where f.getADeclaration() = v
select f.toString(), v