mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge remote-tracking branch 'upstream/master' into dbartol/MissingToString
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/japanese-era/exact-era-date
|
||||
* @precision medium
|
||||
* @precision low
|
||||
* @tags reliability
|
||||
* japanese-era
|
||||
*/
|
||||
|
||||
@@ -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.)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
@@ -1,5 +0,0 @@
|
||||
import cpp
|
||||
|
||||
from File f
|
||||
where f.toString() != ""
|
||||
select f.toString(), f.getRelativePath()
|
||||
4
cpp/ql/test/library-tests/files/Files.expected
Normal file
4
cpp/ql/test/library-tests/files/Files.expected
Normal 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 |
|
||||
18
cpp/ql/test/library-tests/files/Files.ql
Normal file
18
cpp/ql/test/library-tests/files/Files.ql
Normal 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(), ", ")
|
||||
@@ -1 +0,0 @@
|
||||
| files1.h:0:0:0:0 | files1.h | files1.cpp:4:6:4:9 | swap |
|
||||
@@ -1,4 +0,0 @@
|
||||
import cpp
|
||||
|
||||
from HeaderFile f
|
||||
select f, f.getATopLevelDeclaration()
|
||||
@@ -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 |
|
||||
@@ -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()
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
@@ -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 |
|
||||
@@ -1,5 +0,0 @@
|
||||
import cpp
|
||||
|
||||
from File f, LocalVariable v
|
||||
where f.getADeclaration() = v
|
||||
select f.toString(), v
|
||||
Reference in New Issue
Block a user