mirror of
https://github.com/github/codeql.git
synced 2026-05-18 05:07:06 +02:00
Compare commits
1 Commits
codeql-cli
...
nickrolfe/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11bcad37af |
@@ -11,14 +11,13 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
1. **Directory structure**
|
||||
|
||||
There are six language-specific query directories in this repository:
|
||||
There are five language-specific query directories in this repository:
|
||||
|
||||
* C/C++: `cpp/ql/src`
|
||||
* C#: `csharp/ql/src`
|
||||
* Java: `java/ql/src`
|
||||
* JavaScript: `javascript/ql/src`
|
||||
* Python: `python/ql/src`
|
||||
* Ruby: `ruby/ql/src`
|
||||
|
||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The QL library `semmle.code.cpp.commons.Exclusions` now contains a predicate
|
||||
`isFromSystemMacroDefinition` for identifying code that originates from a
|
||||
macro outside the project being analyzed.
|
||||
@@ -237,7 +237,7 @@ class Class extends UserType {
|
||||
exists(ClassDerivation cd | cd.getBaseClass() = base |
|
||||
result =
|
||||
this.accessOfBaseMemberMulti(cd.getDerivedClass(),
|
||||
fieldInBase.accessInDirectDerived(cd.getASpecifier()))
|
||||
fieldInBase.accessInDirectDerived(cd.getASpecifier().(AccessSpecifier)))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -261,7 +261,8 @@ class Class extends UserType {
|
||||
* includes the case of `base` = `this`.
|
||||
*/
|
||||
AccessSpecifier accessOfBaseMember(Declaration member) {
|
||||
result = this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier())
|
||||
result =
|
||||
this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,7 +319,7 @@ class Class extends UserType {
|
||||
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
|
||||
// Note: Overload resolution is not implemented -- all copy
|
||||
// constructors are considered equal.
|
||||
this.cannotAccessCopyConstructorOnAny(t)
|
||||
this.cannotAccessCopyConstructorOnAny(t.(Class))
|
||||
)
|
||||
or
|
||||
// - T has direct or virtual base class that cannot be copied (has deleted,
|
||||
@@ -391,7 +392,7 @@ class Class extends UserType {
|
||||
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
|
||||
// Note: Overload resolution is not implemented -- all copy assignment
|
||||
// operators are considered equal.
|
||||
this.cannotAccessCopyAssignmentOperatorOnAny(t)
|
||||
this.cannotAccessCopyAssignmentOperatorOnAny(t.(Class))
|
||||
)
|
||||
or
|
||||
exists(Class c | c = this.getADirectOrVirtualBase() |
|
||||
|
||||
@@ -490,7 +490,8 @@ class AccessHolder extends Declaration, TAccessHolder {
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate canAccessMember(Declaration member, Class derived) {
|
||||
this.couldAccessMember(member.getDeclaringType(), member.getASpecifier(), derived)
|
||||
this.couldAccessMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier),
|
||||
derived)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -275,7 +275,7 @@ private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest)
|
||||
dependsOnTransitive(src, mid) and
|
||||
not mid instanceof Type and
|
||||
not mid instanceof EnumConstant and
|
||||
getDeclarationEntries(mid, dest) and
|
||||
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
|
||||
not dest instanceof TypeDeclarationEntry
|
||||
)
|
||||
or
|
||||
@@ -283,9 +283,9 @@ private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest)
|
||||
// dependency from a Type / Variable / Function use -> any (visible) definition
|
||||
dependsOnTransitive(src, mid) and
|
||||
not mid instanceof EnumConstant and
|
||||
getDeclarationEntries(mid, dest) and
|
||||
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
|
||||
// must be definition
|
||||
dest.isDefinition()
|
||||
dest.(DeclarationEntry).isDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ private predicate dependsOnFull(DependsSource src, Symbol dest, int category) {
|
||||
// dependency from a Variable / Function use -> non-visible definition (link time)
|
||||
dependsOnTransitive(src, mid) and
|
||||
not mid instanceof EnumConstant and
|
||||
getDeclarationEntries(mid, dest) and
|
||||
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
|
||||
not dest instanceof TypeDeclarationEntry and
|
||||
// must be definition
|
||||
dest.(DeclarationEntry).isDefinition() and
|
||||
|
||||
@@ -81,8 +81,8 @@ predicate functionContainsPreprocCode(Function f) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is completely or partially from a macro invocation `mi`, as
|
||||
* opposed to being passed in as an argument.
|
||||
* Holds if `e` is completely or partially from a macro definition, as opposed
|
||||
* to being passed in as an argument.
|
||||
*
|
||||
* In the following example, the call to `f` is from a macro definition,
|
||||
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
|
||||
@@ -93,8 +93,8 @@ predicate functionContainsPreprocCode(Function f) {
|
||||
* M(y + 1);
|
||||
* ```
|
||||
*/
|
||||
private predicate isFromMacroInvocation(Element e, MacroInvocation mi) {
|
||||
exists(Location eLocation, Location miLocation |
|
||||
predicate isFromMacroDefinition(Element e) {
|
||||
exists(MacroInvocation mi, Location eLocation, Location miLocation |
|
||||
mi.getAnExpandedElement() = e and
|
||||
eLocation = e.getLocation() and
|
||||
miLocation = mi.getLocation() and
|
||||
@@ -109,36 +109,3 @@ private predicate isFromMacroInvocation(Element e, MacroInvocation mi) {
|
||||
eLocation.getEndColumn() >= miLocation.getEndColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is completely or partially from a macro definition, as opposed
|
||||
* to being passed in as an argument.
|
||||
*
|
||||
* In the following example, the call to `f` is from a macro definition,
|
||||
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
|
||||
* from `M` refers to a macro.
|
||||
* ```
|
||||
* #define M(x) f(x)
|
||||
* ...
|
||||
* M(y + 1);
|
||||
* ```
|
||||
*/
|
||||
predicate isFromMacroDefinition(Element e) { isFromMacroInvocation(e, _) }
|
||||
|
||||
/**
|
||||
* Holds if `e` is completely or partially from a _system macro_ definition, as
|
||||
* opposed to being passed in as an argument. A system macro is a macro whose
|
||||
* definition is outside the source directory of the database.
|
||||
*
|
||||
* If the system macro is invoked through a non-system macro, then this
|
||||
* predicate does not hold.
|
||||
*
|
||||
* See also `isFromMacroDefinition`.
|
||||
*/
|
||||
predicate isFromSystemMacroDefinition(Element e) {
|
||||
exists(MacroInvocation mi |
|
||||
isFromMacroInvocation(e, mi) and
|
||||
// Has no relative path in the database, meaning it's a system file.
|
||||
not exists(mi.getMacro().getFile().getRelativePath())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,19 +11,6 @@ private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[n1, n2]
|
||||
private predicate controlFlowNodeSuccessorTransitive(ControlFlowNode n1, ControlFlowNode n2) {
|
||||
exists(BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
|
||||
pragma[only_bind_into](bb1).getNode(pos1) = n1 and
|
||||
pragma[only_bind_into](bb2).getNode(pos2) = n2 and
|
||||
(
|
||||
bb1 = bb2 and pos1 < pos2
|
||||
or
|
||||
bb1.getASuccessor+() = bb2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression `e` may add a null terminator to the string in
|
||||
* variable `v`.
|
||||
@@ -43,9 +30,14 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
|
||||
)
|
||||
or
|
||||
// Assignment to another stack variable
|
||||
exists(Expr e0 |
|
||||
mayAddNullTerminatorHelper(pragma[only_bind_into](e), va, pragma[only_bind_into](e0)) and
|
||||
controlFlowNodeSuccessorTransitive(e, e0)
|
||||
exists(Expr e0, BasicBlock bb, int pos, BasicBlock bb0, int pos0 |
|
||||
mayAddNullTerminatorHelper(e, va, e0) and
|
||||
bb.getNode(pos) = e and
|
||||
bb0.getNode(pos0) = e0
|
||||
|
|
||||
bb = bb0 and pos < pos0
|
||||
or
|
||||
bb.getASuccessor+() = bb0
|
||||
)
|
||||
or
|
||||
// Assignment to non-stack variable
|
||||
@@ -127,9 +119,14 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
|
||||
variableMustBeNullTerminated(use) and
|
||||
// Simplified: check that `p` may not be null terminated on *any*
|
||||
// path to `use` (including the one found via `parameterUsePair`)
|
||||
not exists(Expr e |
|
||||
mayAddNullTerminator(pragma[only_bind_into](e), p.getAnAccess()) and
|
||||
controlFlowNodeSuccessorTransitive(e, use)
|
||||
not exists(Expr e, BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
|
||||
mayAddNullTerminator(e, p.getAnAccess()) and
|
||||
bb1.getNode(pos1) = e and
|
||||
bb2.getNode(pos2) = use
|
||||
|
|
||||
bb1 = bb2 and pos1 < pos2
|
||||
or
|
||||
bb1.getASuccessor+() = bb2
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -175,7 +175,9 @@ class FormattingFunctionCall extends Expr {
|
||||
/**
|
||||
* Gets the index at which the format string occurs in the argument list.
|
||||
*/
|
||||
int getFormatParameterIndex() { result = this.getTarget().getFormatParameterIndex() }
|
||||
int getFormatParameterIndex() {
|
||||
result = this.getTarget().(FormattingFunction).getFormatParameterIndex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the format expression used in this call.
|
||||
@@ -189,7 +191,7 @@ class FormattingFunctionCall extends Expr {
|
||||
exists(int i |
|
||||
result = this.getArgument(i) and
|
||||
n >= 0 and
|
||||
n = i - this.getTarget().getFirstFormatArgumentIndex()
|
||||
n = i - this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -249,7 +251,7 @@ class FormattingFunctionCall extends Expr {
|
||||
int getNumFormatArgument() {
|
||||
result = count(this.getFormatArgument(_)) and
|
||||
// format arguments must be known
|
||||
exists(this.getTarget().getFirstFormatArgumentIndex())
|
||||
exists(this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,27 +289,35 @@ class FormatLiteral extends Literal {
|
||||
* a `char *` (either way, `%S` will have the opposite meaning).
|
||||
* DEPRECATED: Use getDefaultCharType() instead.
|
||||
*/
|
||||
deprecated predicate isWideCharDefault() { this.getUse().getTarget().isWideCharDefault() }
|
||||
deprecated predicate isWideCharDefault() {
|
||||
this.getUse().getTarget().(FormattingFunction).isWideCharDefault()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default character type expected for `%s` by this format literal. Typically
|
||||
* `char` or `wchar_t`.
|
||||
*/
|
||||
Type getDefaultCharType() { result = this.getUse().getTarget().getDefaultCharType() }
|
||||
Type getDefaultCharType() {
|
||||
result = this.getUse().getTarget().(FormattingFunction).getDefaultCharType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-default character type expected for `%S` by this format literal. Typically
|
||||
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
|
||||
* which is correct for a particular function.
|
||||
*/
|
||||
Type getNonDefaultCharType() { result = this.getUse().getTarget().getNonDefaultCharType() }
|
||||
Type getNonDefaultCharType() {
|
||||
result = this.getUse().getTarget().(FormattingFunction).getNonDefaultCharType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the wide character type for this format literal. This is usually `wchar_t`. On some
|
||||
* snapshots there may be multiple results where we can't tell which is correct for a
|
||||
* particular function.
|
||||
*/
|
||||
Type getWideCharType() { result = this.getUse().getTarget().getWideCharType() }
|
||||
Type getWideCharType() {
|
||||
result = this.getUse().getTarget().(FormattingFunction).getWideCharType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `FormatLiteral` is in a context that supports
|
||||
@@ -886,7 +896,7 @@ class FormatLiteral extends Literal {
|
||||
exists(string len, string conv |
|
||||
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
this.getUse().getTarget().getFormatCharType().getSize() > 1 and // wide function
|
||||
this.getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function
|
||||
(
|
||||
conv = "c" and
|
||||
result = this.getNonDefaultCharType()
|
||||
|
||||
@@ -25,7 +25,7 @@ predicate definitionUsePair(SemanticStackVariable var, Expr def, Expr use) {
|
||||
* Holds if the definition `def` of some stack variable can reach `node`, which
|
||||
* is a definition or use, without crossing definitions of the same variable.
|
||||
*/
|
||||
predicate definitionReaches(Expr def, Expr node) { def.(Def).reaches(true, _, node) }
|
||||
predicate definitionReaches(Expr def, Expr node) { def.(Def).reaches(true, _, node.(DefOrUse)) }
|
||||
|
||||
private predicate hasAddressOfAccess(SemanticStackVariable var) {
|
||||
var.getAnAccess().isAddressOfAccessNonConst()
|
||||
|
||||
@@ -62,7 +62,7 @@ class SsaDefinition extends ControlFlowNodeBase {
|
||||
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
|
||||
|
||||
/** Holds if this definition is a phi node for variable `v`. */
|
||||
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this)) }
|
||||
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
|
||||
|
||||
/** Gets the location of this definition. */
|
||||
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
|
||||
|
||||
@@ -292,7 +292,7 @@ library class SSAHelper extends int {
|
||||
*/
|
||||
cached
|
||||
string toString(ControlFlowNode node, StackVariable v) {
|
||||
if phi_node(v, node)
|
||||
if phi_node(v, node.(BasicBlock))
|
||||
then result = "SSA phi(" + v.getName() + ")"
|
||||
else (
|
||||
ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")"
|
||||
|
||||
@@ -231,7 +231,7 @@ private class PostOrderInitializer extends Initializer {
|
||||
or
|
||||
this.getDeclaration() = for.getRangeVariable()
|
||||
or
|
||||
this.getDeclaration() = for.getBeginEndDeclaration().getADeclaration()
|
||||
this.getDeclaration() = for.getBeginEndDeclaration().(DeclStmt).getADeclaration()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1143,7 +1143,7 @@ private class ExceptionSource extends Node {
|
||||
this.reachesParent(mid) and
|
||||
not mid = any(TryStmt try).getStmt() and
|
||||
not mid = any(MicrosoftTryStmt try).getStmt() and
|
||||
parent = mid.getParentNode()
|
||||
parent = mid.(Node).getParentNode()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ library class ExprEvaluator extends int {
|
||||
this.interestingInternal(e, req, true) and
|
||||
(
|
||||
result = req.(CompileTimeConstantInt).getIntValue() or
|
||||
result = this.getCompoundValue(e, req)
|
||||
result = this.getCompoundValue(e, req.(CompileTimeVariableExpr))
|
||||
) and
|
||||
(
|
||||
req.getUnderlyingType().(IntegralType).isSigned() or
|
||||
@@ -611,7 +611,7 @@ library class ExprEvaluator extends int {
|
||||
or
|
||||
exists(AssignExpr req | req = val | result = this.getValueInternal(e, req.getRValue()))
|
||||
or
|
||||
result = this.getVariableValue(e, val)
|
||||
result = this.getVariableValue(e, val.(VariableAccess))
|
||||
or
|
||||
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
||||
result = this.getFunctionValue(call.getTarget())
|
||||
@@ -663,7 +663,7 @@ library class ExprEvaluator extends int {
|
||||
this.interestingInternal(_, req, false) and
|
||||
(
|
||||
result = req.(CompileTimeConstantInt).getIntValue() or
|
||||
result = this.getCompoundValueNonSubExpr(req)
|
||||
result = this.getCompoundValueNonSubExpr(req.(CompileTimeVariableExpr))
|
||||
) and
|
||||
(
|
||||
req.getUnderlyingType().(IntegralType).isSigned() or
|
||||
@@ -787,7 +787,7 @@ library class ExprEvaluator extends int {
|
||||
or
|
||||
exists(AssignExpr req | req = val | result = this.getValueInternalNonSubExpr(req.getRValue()))
|
||||
or
|
||||
result = this.getVariableValueNonSubExpr(val)
|
||||
result = this.getVariableValueNonSubExpr(val.(VariableAccess))
|
||||
or
|
||||
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
||||
result = this.getFunctionValue(call.getTarget())
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -2,42 +2,6 @@ private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
import Cached
|
||||
|
||||
module DataFlowImplCommonPublic {
|
||||
private newtype TFlowFeature =
|
||||
TFeatureHasSourceCallContext() or
|
||||
TFeatureHasSinkCallContext() or
|
||||
TFeatureEqualSourceSinkCallContext()
|
||||
|
||||
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
|
||||
class FlowFeature extends TFlowFeature {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that sources have some existing
|
||||
* call context.
|
||||
*/
|
||||
class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
|
||||
override string toString() { result = "FeatureHasSourceCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that sinks have some existing
|
||||
* call context.
|
||||
*/
|
||||
class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
|
||||
override string toString() { result = "FeatureHasSinkCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that source-sink pairs have some
|
||||
* shared existing call context.
|
||||
*/
|
||||
class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
|
||||
override string toString() { result = "FeatureEqualSourceSinkCallContext" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
|
||||
*
|
||||
@@ -287,7 +251,7 @@ private module Cached {
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
cached
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
|
||||
|
||||
cached
|
||||
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
|
||||
@@ -352,7 +316,9 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
|
||||
predicate parameterNode(Node n, DataFlowCallable c, int i) {
|
||||
n.(ParameterNode).isParameterOf(c, i)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate argumentNode(Node n, DataFlowCall call, int pos) {
|
||||
|
||||
@@ -31,7 +31,7 @@ module Consistency {
|
||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c = count(n.getEnclosingCallable()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
@@ -85,13 +85,13 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
|
||||
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
|
||||
msg = "Callable mismatch for parameter."
|
||||
}
|
||||
|
||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||
simpleLocalFlowStep(n1, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ module Consistency {
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
c = nodeGetEnclosingCallable(n) and
|
||||
c = n.getEnclosingCallable() and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||
@@ -120,7 +120,7 @@ module Consistency {
|
||||
n.(ArgumentNode).argumentOf(call, _) and
|
||||
msg = "ArgumentNode and call does not share enclosing callable."
|
||||
) and
|
||||
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
|
||||
n.getEnclosingCallable() != call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
// This predicate helps the compiler forget that in some languages
|
||||
@@ -151,7 +151,7 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
|
||||
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
|
||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -3,12 +3,6 @@ private import DataFlowUtil
|
||||
private import DataFlowDispatch
|
||||
private import FlowVar
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
|
||||
|
||||
/** Gets the instance argument of a non-static call. */
|
||||
private Node getInstanceArgument(Call call) {
|
||||
result.asExpr() = call.getQualifier()
|
||||
|
||||
@@ -31,7 +31,7 @@ class Expr extends StmtParent, @expr {
|
||||
override Stmt getEnclosingStmt() {
|
||||
result = this.getParent().(Expr).getEnclosingStmt()
|
||||
or
|
||||
result = this.getParent()
|
||||
result = this.getParent().(Stmt)
|
||||
or
|
||||
exists(Expr other | result = other.getEnclosingStmt() and other.getConversion() = this)
|
||||
or
|
||||
|
||||
@@ -31,7 +31,7 @@ private predicate addressConstantVariable(Variable v) {
|
||||
private predicate constantAddressLValue(Expr lvalue) {
|
||||
lvalue.(VariableAccess).getTarget() =
|
||||
any(Variable v |
|
||||
v.isStatic()
|
||||
v.(Variable).isStatic()
|
||||
or
|
||||
v instanceof GlobalOrNamespaceVariable
|
||||
)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -2,42 +2,6 @@ private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
import Cached
|
||||
|
||||
module DataFlowImplCommonPublic {
|
||||
private newtype TFlowFeature =
|
||||
TFeatureHasSourceCallContext() or
|
||||
TFeatureHasSinkCallContext() or
|
||||
TFeatureEqualSourceSinkCallContext()
|
||||
|
||||
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
|
||||
class FlowFeature extends TFlowFeature {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that sources have some existing
|
||||
* call context.
|
||||
*/
|
||||
class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
|
||||
override string toString() { result = "FeatureHasSourceCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that sinks have some existing
|
||||
* call context.
|
||||
*/
|
||||
class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
|
||||
override string toString() { result = "FeatureHasSinkCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that source-sink pairs have some
|
||||
* shared existing call context.
|
||||
*/
|
||||
class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
|
||||
override string toString() { result = "FeatureEqualSourceSinkCallContext" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
|
||||
*
|
||||
@@ -287,7 +251,7 @@ private module Cached {
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
cached
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
|
||||
|
||||
cached
|
||||
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
|
||||
@@ -352,7 +316,9 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
|
||||
predicate parameterNode(Node n, DataFlowCallable c, int i) {
|
||||
n.(ParameterNode).isParameterOf(c, i)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate argumentNode(Node n, DataFlowCall call, int pos) {
|
||||
|
||||
@@ -31,7 +31,7 @@ module Consistency {
|
||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c = count(n.getEnclosingCallable()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
@@ -85,13 +85,13 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
|
||||
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
|
||||
msg = "Callable mismatch for parameter."
|
||||
}
|
||||
|
||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||
simpleLocalFlowStep(n1, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ module Consistency {
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
c = nodeGetEnclosingCallable(n) and
|
||||
c = n.getEnclosingCallable() and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||
@@ -120,7 +120,7 @@ module Consistency {
|
||||
n.(ArgumentNode).argumentOf(call, _) and
|
||||
msg = "ArgumentNode and call does not share enclosing callable."
|
||||
) and
|
||||
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
|
||||
n.getEnclosingCallable() != call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
// This predicate helps the compiler forget that in some languages
|
||||
@@ -151,7 +151,7 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
|
||||
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
|
||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,6 @@ private import DataFlowUtil
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowDispatch
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Instance arguments (`this` pointer) and read side effects
|
||||
@@ -194,7 +188,7 @@ private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode
|
||||
exists(StoreInstruction store, Class c |
|
||||
store = node2.asInstruction() and
|
||||
store.getSourceValueOperand() = node1.asOperand() and
|
||||
getWrittenField(store, f.getAField(), c) and
|
||||
getWrittenField(store, f.(FieldContent).getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1032,7 +1032,7 @@ abstract class TranslatedConversion extends TranslatedNonConstantExpr {
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
|
||||
|
||||
final TranslatedExpr getOperand() { result = getTranslatedExpr(expr.getExpr()) }
|
||||
final TranslatedExpr getOperand() { result = getTranslatedExpr(expr.(Conversion).getExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1305,9 +1305,9 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
|
||||
}
|
||||
|
||||
override Opcode getOpcode() {
|
||||
result = binaryArithmeticOpcode(expr) or
|
||||
result = binaryBitwiseOpcode(expr) or
|
||||
result = comparisonOpcode(expr)
|
||||
result = binaryArithmeticOpcode(expr.(BinaryArithmeticOperation)) or
|
||||
result = binaryBitwiseOpcode(expr.(BinaryBitwiseOperation)) or
|
||||
result = comparisonOpcode(expr.(ComparisonOperation))
|
||||
}
|
||||
|
||||
override int getInstructionElementSize(InstructionTag tag) {
|
||||
|
||||
@@ -103,7 +103,9 @@ class TranslatedDeclStmt extends TranslatedStmt {
|
||||
class TranslatedExprStmt extends TranslatedStmt {
|
||||
override ExprStmt stmt;
|
||||
|
||||
TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr().getFullyConverted()) }
|
||||
TranslatedExpr getExpr() {
|
||||
result = getTranslatedExpr(stmt.(ExprStmt).getExpr().getFullyConverted())
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = getExpr() }
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ abstract class Architecture extends string {
|
||||
or
|
||||
t instanceof LongLongType and result = this.longLongSize()
|
||||
or
|
||||
result = this.enumBitSize(t)
|
||||
result = this.enumBitSize(t.(Enum))
|
||||
or
|
||||
result = this.integralBitSize(t.(SpecifiedType).getBaseType())
|
||||
or
|
||||
@@ -183,7 +183,7 @@ abstract class Architecture extends string {
|
||||
or
|
||||
t instanceof ReferenceType and result = this.pointerSize()
|
||||
or
|
||||
result = this.enumAlignment(t)
|
||||
result = this.enumAlignment(t.(Enum))
|
||||
or
|
||||
result = this.alignment(t.(SpecifiedType).getBaseType())
|
||||
or
|
||||
@@ -232,14 +232,14 @@ private Field getAnInitialField(PaddedType t) {
|
||||
result = t.getAField()
|
||||
or
|
||||
// Initial field of the type of a field of the union
|
||||
result = getAnInitialField(t.getAField().getUnspecifiedType())
|
||||
result = getAnInitialField(t.getAField().getUnspecifiedType().(PaddedType))
|
||||
else
|
||||
exists(Field firstField | t.fieldIndex(firstField) = 1 |
|
||||
// The first field of `t`
|
||||
result = firstField
|
||||
or
|
||||
// Initial field of the first field of `t`
|
||||
result = getAnInitialField(firstField.getUnspecifiedType())
|
||||
result = getAnInitialField(firstField.getUnspecifiedType().(PaddedType))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
|
||||
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
|
||||
|
||||
/** Whether this definition is a phi node for variable `v`. */
|
||||
predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this)) }
|
||||
predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this.(BasicBlock))) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use isGuardPhi/4 instead
|
||||
|
||||
@@ -173,6 +173,6 @@ private predicate fileWriteWithConvChar(FormattingFunctionCall ffc, Expr source,
|
||||
source = ffc.getFormatArgument(n)
|
||||
|
|
||||
exists(f.getOutputParameterIndex(true)) and
|
||||
conv = ffc.getFormat().(FormatLiteral).getConversionChar(n)
|
||||
conv = ffc.(FormattingFunctionCall).getFormat().(FormatLiteral).getConversionChar(n)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -589,7 +589,7 @@ private predicate mk_HasAlloc(HashCons hc, NewOrNewArrayExpr new) {
|
||||
}
|
||||
|
||||
private predicate mk_HasExtent(HashCons hc, NewArrayExpr new) {
|
||||
hc = hashCons(new.getExtent().getFullyConverted())
|
||||
hc = hashCons(new.(NewArrayExpr).getExtent().getFullyConverted())
|
||||
}
|
||||
|
||||
private predicate analyzableNewExpr(NewExpr new) {
|
||||
@@ -619,7 +619,7 @@ private predicate analyzableNewArrayExpr(NewArrayExpr new) {
|
||||
strictcount(new.getAllocatedType().getUnspecifiedType()) = 1 and
|
||||
count(new.getAllocatorCall().getFullyConverted()) <= 1 and
|
||||
count(new.getInitializer().getFullyConverted()) <= 1 and
|
||||
count(new.getExtent().getFullyConverted()) <= 1
|
||||
count(new.(NewArrayExpr).getExtent().getFullyConverted()) <= 1
|
||||
}
|
||||
|
||||
private predicate mk_NewArrayExpr(
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>about General Class-Level Information</p>
|
||||
|
||||
<!--TOC-->
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
13
cpp/ql/src/Architecture/index.qhelp
Normal file
13
cpp/ql/src/Architecture/index.qhelp
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>about Architecture</p>
|
||||
|
||||
<!--TOC-->
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
@@ -81,8 +81,9 @@ class BlockOrNonChild extends Element {
|
||||
predicate emptyBlockContainsNonchild(BlockStmt b) {
|
||||
emptyBlock(_, b) and
|
||||
exists(BlockOrNonChild c, AffectedFile file |
|
||||
c.getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
|
||||
c.getNonContiguousEndRankIn(file) < b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
|
||||
c.(BlockOrNonChild).getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
|
||||
c.(BlockOrNonChild).getNonContiguousEndRankIn(file) <
|
||||
b.(BlockOrNonChild).getNonContiguousEndRankIn(file)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class Thing extends Locatable {
|
||||
}
|
||||
|
||||
Thing callsOrAccesses() {
|
||||
this.(Function).calls(result)
|
||||
this.(Function).calls(result.(Function))
|
||||
or
|
||||
this.(Function).accesses(result.(Function))
|
||||
or
|
||||
|
||||
16
cpp/ql/src/Best Practices/index.qhelp
Normal file
16
cpp/ql/src/Best Practices/index.qhelp
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>about best practices</p>
|
||||
|
||||
<!--TOC-->
|
||||
|
||||
|
||||
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
@@ -63,14 +63,14 @@ predicate cannotContainString(Type t) {
|
||||
|
||||
predicate isNonConst(DataFlow::Node node) {
|
||||
exists(Expr e | e = node.asExpr() |
|
||||
exists(FunctionCall fc | fc = e |
|
||||
exists(FunctionCall fc | fc = e.(FunctionCall) |
|
||||
not (
|
||||
whitelistFunction(fc.getTarget(), _) or
|
||||
fc.getTarget().hasDefinition()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Parameter p | p = e.(VariableAccess).getTarget() |
|
||||
exists(Parameter p | p = e.(VariableAccess).getTarget().(Parameter) |
|
||||
p.getFunction().getName() = "main" and p.getType() instanceof PointerType
|
||||
)
|
||||
or
|
||||
|
||||
@@ -10,7 +10,7 @@ import semmle.code.cpp.commons.DateTime
|
||||
* Get the top-level `BinaryOperation` enclosing the expression e.
|
||||
*/
|
||||
private BinaryOperation getATopLevelBinaryOperationExpression(Expr e) {
|
||||
result = e.getEnclosingElement()
|
||||
result = e.getEnclosingElement().(BinaryOperation)
|
||||
or
|
||||
result = getATopLevelBinaryOperationExpression(e.getEnclosingElement())
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ predicate functionDefinedInIfDefRecursive(Function f) {
|
||||
*/
|
||||
predicate baseCall(FunctionCall call) {
|
||||
call.getNameQualifier().getQualifyingElement() =
|
||||
call.getEnclosingFunction().getDeclaringType().getABaseClass+()
|
||||
call.getEnclosingFunction().getDeclaringType().(Class).getABaseClass+()
|
||||
}
|
||||
|
||||
from PureExprInVoidContext peivc, Locatable parent, Locatable info, string info_text, string tail
|
||||
|
||||
@@ -15,7 +15,7 @@ import cpp
|
||||
from File f, float complexity, float loc
|
||||
where
|
||||
f.fromSource() and
|
||||
loc = sum(FunctionDeclarationEntry fde | fde.getFile() = f | fde.getNumberOfLines()) and
|
||||
loc = sum(FunctionDeclarationEntry fde | fde.getFile() = f | fde.getNumberOfLines()).(float) and
|
||||
if loc > 0
|
||||
then
|
||||
// Weighted average of complexity by function length
|
||||
|
||||
42
cpp/ql/src/Metrics/History/HChurn.qhelp
Normal file
42
cpp/ql/src/Metrics/History/HChurn.qhelp
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This metric measures the number of lines of text that have been added, deleted
|
||||
or modified in files below this location in the tree.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Code churn is known to be a good (if not the best) predictor of defects in a
|
||||
code component (see e.g. [Nagappan] or [Khoshgoftaar]). The intuition is that
|
||||
files, packages or projects that have experienced a disproportionately high
|
||||
amount of churn for the amount of code involved may have been harder to write,
|
||||
and are thus likely to contain more bugs.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
It is a fact of life that some code is going to be changed more than the rest,
|
||||
and little can be done to change this. However, bearing in mind code churn's
|
||||
effectiveness as a defect predictor, code that has been repeatedly changed
|
||||
should be subjected to vigorous testing and code review.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
|
||||
<li>
|
||||
N. Nagappan et al. <em>Change Bursts as Defect Predictors</em>. In Proceedings of the 21st IEEE International Symposium on Software Reliability Engineering, 2010.
|
||||
</li>
|
||||
<li>
|
||||
T. M. Khoshgoftaar and R. M. Szabo. <em>Improving code churn predictions during the system test and maintenance phases</em>. In ICSM '94, 1994, pp. 58-67.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
6
cpp/ql/src/Metrics/History/HLinesAdded.qhelp
Normal file
6
cpp/ql/src/Metrics/History/HLinesAdded.qhelp
Normal file
@@ -0,0 +1,6 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="HChurn.qhelp" />
|
||||
</qhelp>
|
||||
6
cpp/ql/src/Metrics/History/HLinesDeleted.qhelp
Normal file
6
cpp/ql/src/Metrics/History/HLinesDeleted.qhelp
Normal file
@@ -0,0 +1,6 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="HChurn.qhelp" />
|
||||
</qhelp>
|
||||
48
cpp/ql/src/Metrics/History/HNumberOfAuthors.qhelp
Normal file
48
cpp/ql/src/Metrics/History/HNumberOfAuthors.qhelp
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This metric measures the number of different authors (by examining the
|
||||
version control history)
|
||||
for files below this location in the tree. (This is a better version
|
||||
of the metric that counts the number of different authors using Javadoc
|
||||
tags.)
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Files that have been changed by a large number of different authors are
|
||||
by definition the product of many minds. New authors working on a file
|
||||
may be less familiar with the design and implementation of the code than
|
||||
the original authors, which can be a potential source of bugs. Furthermore,
|
||||
code that has been worked on by many people, if not carefully maintained,
|
||||
often ends up lacking conceptual integrity. For both of these reasons, any
|
||||
code that has been worked on by an unusually high number of different people
|
||||
merits careful inspection in code reviews.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
There is clearly no way to reduce the number of authors that have worked
|
||||
on a file - it is impossible to rewrite history. However, files highlighted
|
||||
by this metric should be given special attention in a code review, and may
|
||||
ultimately be good candidates for refactoring/rewriting by an individual,
|
||||
experienced developer.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
|
||||
<li>
|
||||
F. P. Brooks Jr. <em>The Mythical Man-Month</em>, Chapter 4. Addison-Wesley, 1974.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
30
cpp/ql/src/Metrics/History/HNumberOfChanges.qhelp
Normal file
30
cpp/ql/src/Metrics/History/HNumberOfChanges.qhelp
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This metric measures the total number of file-level changes made to files
|
||||
below this location in the tree. For an individual file, it measures the
|
||||
number of commits that have affected that file. For a directory of files, it
|
||||
measures the sum of the file-level changes for each of the files in the
|
||||
directory.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example, suppose we have a directory containing two files, A and B. If the
|
||||
number of file-level changes to A is <code>100</code>, and the number of
|
||||
file-level changes to B is <code>80</code>, then the total number of
|
||||
file-level changes to the directory is <code>180</code>. Note that this is
|
||||
likely to be different (in some cases very different) from the number of
|
||||
commits that affected any file in the directory, since more than one file can
|
||||
be changed by a single commit. (Note what would happen if we performed
|
||||
<code>80</code> commits on A and B, followed by another <code>20</code>
|
||||
commits on A alone - the total number of file-level changes would be
|
||||
<code>180</code>, but the number of commits involved would be
|
||||
<code>100</code>.)
|
||||
</p>
|
||||
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
51
cpp/ql/src/Metrics/History/HNumberOfCoCommits.qhelp
Normal file
51
cpp/ql/src/Metrics/History/HNumberOfCoCommits.qhelp
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This metric measures the average number of co-committed files for the files
|
||||
below this location in the tree.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A co-committed file is one that is committed at the same time as a given file.
|
||||
For instance, if you commit files A, B and C together, then B and C would be
|
||||
the co-committed files of A for that commit. The value of the metric for an
|
||||
individual file is the average number of such co-committed files over all
|
||||
commits. The value of the metric for a directory is the aggregation of these
|
||||
averages - for instance, if we are using <code>max</code> as our aggregation
|
||||
function, the value would be the maximum of the average number of co-commits
|
||||
over all files in the directory.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An unusually high value for this metric may indicate that the file in question
|
||||
is too tightly-coupled to other files, and it is difficult to change it in
|
||||
isolation. Alternatively, it may just be an indication that you commit lots of
|
||||
unrelated changes at the same time.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Examine the file in question to see what the problem is.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
If the file is too tightly coupled, it will have high values for its afferent
|
||||
and/or efferent coupling metrics, and you should apply the advice given there.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
If the file is not tightly coupled, but you find that you are committing lots
|
||||
of unrelated changes at the same time, then you may want to revisit your commit
|
||||
practices.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</recommendation>
|
||||
</qhelp>
|
||||
53
cpp/ql/src/Metrics/History/HNumberOfReCommits.qhelp
Normal file
53
cpp/ql/src/Metrics/History/HNumberOfReCommits.qhelp
Normal file
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This metric measures the number of file re-commits that have occurred below
|
||||
this location in the tree. A re-commit is taken to mean a commit to a file
|
||||
that was touched less than five days ago.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In a system that is being developed using a controlled change process (where
|
||||
changes are not committed until they are in some sense 'complete'), re-commits
|
||||
can be (but are not always) an indication that an initial change was not
|
||||
successful and had to be revisited within a short time period. The intuition
|
||||
is that the original change may have been difficult to get right, and hence
|
||||
the code in the file may be more than usually defect-prone. The concept is
|
||||
somewhat similar to that of 'change bursts', as described in [Nagappan].
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
High numbers of re-commits can be addressed on two levels: preventative and
|
||||
corrective.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
On the preventative side, a high number of re-commits may be an indication
|
||||
that your code review process needs an overhaul.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
On the corrective side, code that has experienced a high number of re-commits
|
||||
should be vigorously code reviewed and tested.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
|
||||
<li>
|
||||
N. Nagappan et al. <em>Change Bursts as Defect Predictors</em>. In Proceedings of the 21st IEEE International Symposium on Software Reliability Engineering, 2010.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
63
cpp/ql/src/Metrics/History/HNumberOfRecentChanges.qhelp
Normal file
63
cpp/ql/src/Metrics/History/HNumberOfRecentChanges.qhelp
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This metric measures the number of recent changes to files that have occurred
|
||||
below this location in the tree. A recent change is taken to mean a change
|
||||
that has occurred in the last <code>180</code> days.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
All code that has changed a great deal may be more than usually prone to
|
||||
defects, but this is particularly true of code that has been changing
|
||||
dramatically in the recent past, because it has not yet had a chance to be
|
||||
properly field-tested in order to iron out the bugs.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
There is more than one reason why a file may have been changing a lot
|
||||
recently:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
The file may be part of a new subsystem that is being written. New code is
|
||||
always going to change a lot in a short period of time, but it is important
|
||||
to ensure that it is properly code reviewed and unit tested before integrating
|
||||
it into a working product.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The file may be being heavily refactored. Large refactorings are sometimes
|
||||
essential, but they are also quite risky. You should write proper regression
|
||||
tests before starting on a major refactoring, and check that they still pass
|
||||
once you're done.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
The same bit of code may be being changed repeatedly because it is difficult
|
||||
to get right. Aside from vigorous code reviewing and testing, it may be a good
|
||||
idea to rethink the system design - if something is that hard
|
||||
to get right (and it's not an inherently difficult concept), you might be making life unnecessarily hard for yourself and
|
||||
risking introducing insidious defects.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</recommendation>
|
||||
<references>
|
||||
|
||||
|
||||
<li>
|
||||
N. Nagappan et al. <em>Change Bursts as Defect Predictors</em>. In Proceedings of the 21st IEEE International Symposium on Software Reliability Engineering, 2010.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -324,8 +324,10 @@ abstract class DataOutput extends Element {
|
||||
/**
|
||||
* Data that is output via standard output or standard error.
|
||||
*/
|
||||
class StandardOutput extends DataOutput instanceof OutputWrite {
|
||||
override Expr getASource() { result = OutputWrite.super.getASource() }
|
||||
class StandardOutput extends DataOutput {
|
||||
StandardOutput() { this instanceof OutputWrite }
|
||||
|
||||
override Expr getASource() { result = this.(OutputWrite).getASource() }
|
||||
}
|
||||
|
||||
private predicate socketCallOrIndirect(FunctionCall call) {
|
||||
@@ -376,5 +378,5 @@ class SocketOutput extends DataOutput {
|
||||
from SystemData sd, DataOutput ow
|
||||
where
|
||||
sd.getAnExprIndirect() = ow.getASource() or
|
||||
sd.getAnExprIndirect() = ow.getASource().getAChild*()
|
||||
sd.getAnExprIndirect() = ow.getASource().(Expr).getAChild*()
|
||||
select ow, "This operation exposes system data from $@.", sd, sd.toString()
|
||||
|
||||
@@ -188,7 +188,8 @@ where
|
||||
isBitwiseandBitwise(exp) and
|
||||
isDifferentResults(exp.(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand(), exp,
|
||||
exp.(BinaryBitwiseOperation).getRightOperand()) and
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand(),
|
||||
exp.(BinaryBitwiseOperation),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation)) and
|
||||
msg = "specify the priority with parentheses."
|
||||
select exp, msg
|
||||
|
||||
@@ -142,7 +142,7 @@ class Resource extends MemberVariable {
|
||||
|
||||
predicate acquisitionWithRequiredKind(Assignment acquireAssign, string kind) {
|
||||
// acquireAssign is an assignment to this resource
|
||||
acquireAssign.getLValue() = this.getAnAccess() and
|
||||
acquireAssign.(Assignment).getLValue() = this.getAnAccess() and
|
||||
// Should be in this class, but *any* member method will do
|
||||
this.inSameClass(acquireAssign) and
|
||||
// Check that it is an acquisition function and return the corresponding kind
|
||||
|
||||
@@ -31,7 +31,7 @@ from Variable v, Variable shadowed
|
||||
where
|
||||
not v.getParentScope().(BlockStmt).isInMacroExpansion() and
|
||||
(
|
||||
v.(LocalVariableOrParameter).shadowsGlobal(shadowed) or
|
||||
v.(LocalVariableOrParameter).shadowsGlobal(shadowed.(GlobalVariable)) or
|
||||
localShadowsParameter(v, shadowed) or
|
||||
shadowing(v, shadowed)
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ import cpp
|
||||
from Assignment a, Variable global, Variable local
|
||||
where
|
||||
a.fromSource() and
|
||||
global.getAnAccess() = a.getLValue() and
|
||||
global.getAnAccess() = a.getLValue().(VariableAccess) and
|
||||
local.getAnAccess() = a.getRValue().(AddressOfExpr).getOperand() and
|
||||
local.hasSpecifier("auto") and
|
||||
(
|
||||
|
||||
@@ -49,11 +49,11 @@ class ExposingIntegralUnion extends Union {
|
||||
exists(MemberVariable mv1, MemberVariable mv2, IntegralType mv1tp, IntegralType mv2tp |
|
||||
mv1 = this.getAMemberVariable() and
|
||||
mv2 = this.getAMemberVariable() and
|
||||
mv1tp = mv1.getUnderlyingType() and
|
||||
mv1tp = mv1.getUnderlyingType().(IntegralType) and
|
||||
(
|
||||
mv2tp = mv2.getUnderlyingType()
|
||||
mv2tp = mv2.getUnderlyingType().(IntegralType)
|
||||
or
|
||||
mv2tp = mv2.getUnderlyingType().(ArrayType).getBaseType().getUnderlyingType()
|
||||
mv2tp = mv2.getUnderlyingType().(ArrayType).getBaseType().getUnderlyingType().(IntegralType)
|
||||
) and
|
||||
mv1tp.getSize() > mv2tp.getSize()
|
||||
)
|
||||
|
||||
@@ -14,7 +14,9 @@ import semmle.code.cpp.controlflow.SSA
|
||||
|
||||
select count(SsaDefinition d, StackVariable v, Expr u |
|
||||
d.getAUse(v) = u and
|
||||
not exists(BasicBlock bd, BasicBlock bu | bd.contains(mkElement(d)) and bu.contains(u) |
|
||||
not exists(BasicBlock bd, BasicBlock bu |
|
||||
bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u)
|
||||
|
|
||||
bbStrictlyDominates(bd, bu)
|
||||
or
|
||||
exists(int i, int j |
|
||||
|
||||
@@ -14,7 +14,9 @@ import semmle.code.cpp.rangeanalysis.RangeSSA
|
||||
|
||||
select count(RangeSsaDefinition d, StackVariable v, Expr u |
|
||||
d.getAUse(v) = u and
|
||||
not exists(BasicBlock bd, BasicBlock bu | bd.contains(mkElement(d)) and bu.contains(u) |
|
||||
not exists(BasicBlock bd, BasicBlock bu |
|
||||
bd.contains(mkElement(d).(ControlFlowNode)) and bu.contains(u)
|
||||
|
|
||||
bbStrictlyDominates(bd, bu)
|
||||
or
|
||||
exists(int i, int j |
|
||||
|
||||
@@ -27,4 +27,3 @@
|
||||
| test.cpp:454:18:454:23 | buffer | Variable $@ may not be null terminated. | test.cpp:452:8:452:13 | buffer | buffer |
|
||||
| test.cpp:513:10:513:15 | buffer | Variable $@ may not be null terminated. | test.cpp:511:8:511:13 | buffer | buffer |
|
||||
| test.cpp:519:16:519:21 | buffer | Variable $@ may not be null terminated. | test.cpp:517:8:517:13 | buffer | buffer |
|
||||
| test.cpp:558:10:558:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:553:8:553:14 | buffer2 | buffer2 |
|
||||
|
||||
@@ -536,51 +536,3 @@ void test_printf(char *str)
|
||||
}
|
||||
}
|
||||
|
||||
void test_reassignment()
|
||||
{
|
||||
{
|
||||
char buffer1[1024];
|
||||
char buffer2[1024];
|
||||
char *buffer_ptr = buffer1;
|
||||
|
||||
buffer_ptr = buffer2;
|
||||
strcpy(buffer_ptr, "content"); // null terminates buffer2
|
||||
strdup(buffer2); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer1[1024];
|
||||
char buffer2[1024];
|
||||
char *buffer_ptr = buffer1;
|
||||
|
||||
strcpy(buffer_ptr, "content"); // null terminates buffer1
|
||||
buffer_ptr = buffer2;
|
||||
strdup(buffer2); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer1[1024];
|
||||
char buffer2[1024];
|
||||
char *buffer_ptr = buffer1;
|
||||
|
||||
while (cond())
|
||||
{
|
||||
buffer_ptr = buffer2;
|
||||
strcpy(buffer_ptr, "content"); // null terminates buffer2
|
||||
strdup(buffer2); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char buffer1[1024];
|
||||
char buffer2[1024];
|
||||
char *buffer_ptr = buffer1;
|
||||
|
||||
while (cond())
|
||||
{
|
||||
strcpy(buffer_ptr, "content"); // null terminates buffer1 or buffer2
|
||||
buffer_ptr = buffer2;
|
||||
strdup(buffer2); // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1540,7 +1540,7 @@ module Statements {
|
||||
c =
|
||||
any(NestedCompletion nc |
|
||||
nc.getNestLevel() = 0 and
|
||||
this.throwMayBeUncaught(nc.getOuterCompletion()) and
|
||||
this.throwMayBeUncaught(nc.getOuterCompletion().(ThrowCompletion)) and
|
||||
(
|
||||
// Incompatible exception type: clause itself
|
||||
last = this and
|
||||
|
||||
@@ -427,7 +427,7 @@ private Element interpretElement0(
|
||||
result = t
|
||||
or
|
||||
subtypes = true and
|
||||
result = t.getASubTypeUnbound+()
|
||||
result = t.(UnboundValueOrRefType).getASubTypeUnbound+()
|
||||
) and
|
||||
result = t and
|
||||
name = "" and
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -366,8 +349,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -383,8 +365,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
||||
not outBarrier(node1, config) and
|
||||
not inBarrier(node2, config) and
|
||||
not fullBarrier(node1, config) and
|
||||
not fullBarrier(node2, config) and
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
not fullBarrier(node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -420,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
||||
*/
|
||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||
|
||||
private predicate hasSourceCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSourceCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx(Configuration config) {
|
||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
feature instanceof FeatureEqualSourceSinkCallContext
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 {
|
||||
class ApApprox = Unit;
|
||||
|
||||
@@ -454,7 +421,7 @@ private module Stage1 {
|
||||
not fullBarrier(node, config) and
|
||||
(
|
||||
sourceNode(node, config) and
|
||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
||||
cc = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, cc, config) and
|
||||
@@ -584,7 +551,7 @@ private module Stage1 {
|
||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||
fwdFlow(node, config) and
|
||||
sinkNode(node, config) and
|
||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
||||
toReturn = false
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
localFlowStep(node, mid, config) and
|
||||
@@ -970,8 +937,6 @@ private module Stage2 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1039,7 +1004,7 @@ private module Stage2 {
|
||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1250,7 +1215,7 @@ private module Stage2 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -1651,8 +1616,6 @@ private module Stage3 {
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
|
||||
CcCall ccSomeCall() { result = true }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -1734,7 +1697,7 @@ private module Stage3 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -1945,7 +1908,7 @@ private module Stage3 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -2403,8 +2366,6 @@ private module Stage4 {
|
||||
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
||||
|
||||
private class LocalCc = LocalCallContext;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
@@ -2500,7 +2461,7 @@ private module Stage4 {
|
||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||
flowCand(node, _, config) and
|
||||
sourceNode(node, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
cc = ccNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
@@ -2711,7 +2672,7 @@ private module Stage4 {
|
||||
) {
|
||||
fwdFlow(node, _, _, ap, config) and
|
||||
sinkNode(node, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
toReturn = false and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
@@ -3103,11 +3064,7 @@ private newtype TPathNode =
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage4::revFlow(node, config) and
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3119,10 +3076,17 @@ private newtype TPathNode =
|
||||
)
|
||||
} or
|
||||
TPathNodeSink(NodeEx node, Configuration config) {
|
||||
exists(PathNodeMid sink |
|
||||
sink.isAtSink() and
|
||||
node = sink.getNodeEx() and
|
||||
config = sink.getConfiguration()
|
||||
sinkNode(node, pragma[only_bind_into](config)) and
|
||||
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
// A sink that is also a source ...
|
||||
sourceNode(node, config)
|
||||
or
|
||||
// ... or a sink that can be reached from a source
|
||||
exists(PathNodeMid mid |
|
||||
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3439,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
// an intermediate step to another intermediate node
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink
|
||||
result = this.getSuccMid().projectToSink()
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
result = sink
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, config) and
|
||||
(
|
||||
if hasSourceCallCtx(config)
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
cc instanceof CallContextAny and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap instanceof AccessPathNil
|
||||
}
|
||||
|
||||
predicate isAtSink() {
|
||||
sinkNode(node, config) and
|
||||
ap instanceof AccessPathNil and
|
||||
if hasSinkCallCtx(config)
|
||||
then
|
||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
||||
// is exactly what we need to check. This also implies
|
||||
// `sc instanceof SummaryCtxNone`.
|
||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
||||
// in this case there's never any need to enter a call except to identify
|
||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
||||
// in the call context of the source.
|
||||
sc instanceof SummaryCtxNone or
|
||||
cc instanceof CallContextNoCall
|
||||
else any()
|
||||
}
|
||||
|
||||
PathNodeSink projectToSink() {
|
||||
this.isAtSink() and
|
||||
result.getNodeEx() = node and
|
||||
result.getConfiguration() = unbindConf(config)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3632,7 +3572,7 @@ private predicate pathIntoArg(
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
pragma[noinline]
|
||||
private predicate parameterCand(
|
||||
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
@@ -3673,11 +3613,7 @@ private predicate pathIntoCallable(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
or
|
||||
not exists(TSummaryCtxSome(p, ap)) and
|
||||
sc = TSummaryCtxNone() and
|
||||
// When the call contexts of source and sink needs to match then there's
|
||||
// never any reason to enter a callable except to find a summary. See also
|
||||
// the comment in `PathNodeMid::isAtSink`.
|
||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
||||
sc = TSummaryCtxNone()
|
||||
)
|
||||
|
|
||||
if recordDataFlowCallSite(call, callable)
|
||||
|
||||
@@ -2,42 +2,6 @@ private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
import Cached
|
||||
|
||||
module DataFlowImplCommonPublic {
|
||||
private newtype TFlowFeature =
|
||||
TFeatureHasSourceCallContext() or
|
||||
TFeatureHasSinkCallContext() or
|
||||
TFeatureEqualSourceSinkCallContext()
|
||||
|
||||
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
|
||||
class FlowFeature extends TFlowFeature {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that sources have some existing
|
||||
* call context.
|
||||
*/
|
||||
class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
|
||||
override string toString() { result = "FeatureHasSourceCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that sinks have some existing
|
||||
* call context.
|
||||
*/
|
||||
class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
|
||||
override string toString() { result = "FeatureHasSinkCallContext" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration feature that implies that source-sink pairs have some
|
||||
* shared existing call context.
|
||||
*/
|
||||
class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
|
||||
override string toString() { result = "FeatureEqualSourceSinkCallContext" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
|
||||
*
|
||||
@@ -287,7 +251,7 @@ private module Cached {
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
cached
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
|
||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
|
||||
|
||||
cached
|
||||
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
|
||||
@@ -352,7 +316,9 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
|
||||
predicate parameterNode(Node n, DataFlowCallable c, int i) {
|
||||
n.(ParameterNode).isParameterOf(c, i)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate argumentNode(Node n, DataFlowCall call, int pos) {
|
||||
|
||||
@@ -31,7 +31,7 @@ module Consistency {
|
||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c = count(n.getEnclosingCallable()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
@@ -85,13 +85,13 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
|
||||
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
|
||||
msg = "Callable mismatch for parameter."
|
||||
}
|
||||
|
||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||
simpleLocalFlowStep(n1, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ module Consistency {
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
c = nodeGetEnclosingCallable(n) and
|
||||
c = n.getEnclosingCallable() and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||
@@ -120,7 +120,7 @@ module Consistency {
|
||||
n.(ArgumentNode).argumentOf(call, _) and
|
||||
msg = "ArgumentNode and call does not share enclosing callable."
|
||||
) and
|
||||
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
|
||||
n.getEnclosingCallable() != call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
// This predicate helps the compiler forget that in some languages
|
||||
@@ -151,7 +151,7 @@ module Consistency {
|
||||
}
|
||||
|
||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
|
||||
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
|
||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,6 @@ private import semmle.code.csharp.frameworks.NHibernate
|
||||
private import semmle.code.csharp.frameworks.system.Collections
|
||||
private import semmle.code.csharp.frameworks.system.threading.Tasks
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
|
||||
|
||||
abstract class NodeImpl extends Node {
|
||||
/** Do not call: use `getEnclosingCallable()` instead. */
|
||||
abstract DataFlowCallable getEnclosingCallableImpl();
|
||||
|
||||
@@ -209,7 +209,9 @@ module XmlReader {
|
||||
/** Provides predicates related to `System.Xml.XmlTextReader`. */
|
||||
module XmlTextReader {
|
||||
private class InsecureXmlTextReader extends InsecureXmlProcessing, ObjectCreation {
|
||||
InsecureXmlTextReader() { this.getObjectType().hasQualifiedName("System.Xml.XmlTextReader") }
|
||||
InsecureXmlTextReader() {
|
||||
this.getObjectType().(ValueOrRefType).hasQualifiedName("System.Xml.XmlTextReader")
|
||||
}
|
||||
|
||||
override predicate isUnsafe(string reason) {
|
||||
not exists(Expr xmlResolverVal |
|
||||
|
||||
@@ -697,7 +697,7 @@ private string stubMethod(Method m, Assembly assembly) {
|
||||
if not m.getDeclaringType() instanceof Enum
|
||||
then
|
||||
result =
|
||||
" " + stubModifiers(m) + stubClassName(m.getReturnType()) + " " +
|
||||
" " + stubModifiers(m) + stubClassName(m.(Method).getReturnType()) + " " +
|
||||
stubExplicitImplementation(m) + escapeIfKeyword(m.getUndecoratedName()) +
|
||||
stubGenericMethodParams(m) + "(" + stubParameters(m) + ")" +
|
||||
stubTypeParametersConstraints(m) + stubImplementation(m) + ";\n"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
namespace RequestForgery.Controllers
|
||||
{
|
||||
public class SSRFController : Controller
|
||||
{
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> Bad(string url)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
|
||||
var client = new HttpClient();
|
||||
await client.SendAsync(request);
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> Good(string url)
|
||||
{
|
||||
string baseUrl = "www.mysecuresite.com/";
|
||||
if (url.StartsWith(baseUrl))
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
var client = new HttpClient();
|
||||
await client.SendAsync(request);
|
||||
|
||||
}
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>Directly incorporating user input into a HTTP request without validating the input
|
||||
can facilitate Server Side Request Forgery (SSRF) attacks. In these attacks, the server
|
||||
may be tricked into making a request and interacting with an attacker-controlled server.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>To guard against SSRF attacks, it is advisable to avoid putting user input
|
||||
directly into the request URL. Instead, maintain a list of authorized
|
||||
URLs on the server; then choose from that list based on the user input provided.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>The following example shows an HTTP request parameter being used directly in a forming a
|
||||
new request without validating the input, which facilitates SSRF attacks.
|
||||
It also shows how to remedy the problem by validating the user input against a known fixed string.
|
||||
</p>
|
||||
|
||||
<sample src="RequestForgery.cs" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://owasp.org/www-community/attacks/Server_Side_Request_Forgery">OWASP SSRF</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @name Uncontrolled data used in network request
|
||||
* @description Sending network requests with user-controlled data allows for request forgery attacks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id cs/request-forgery
|
||||
* @tags security
|
||||
* external/cwe/cwe-918
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import RequestForgery::RequestForgery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
|
||||
from RequestForgeryConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where c.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "$@ flows to here and is used in a server side web request.",
|
||||
source.getNode(), "User-provided value"
|
||||
@@ -1,234 +0,0 @@
|
||||
import csharp
|
||||
|
||||
module RequestForgery {
|
||||
import semmle.code.csharp.controlflow.Guards
|
||||
import semmle.code.csharp.frameworks.System
|
||||
import semmle.code.csharp.frameworks.system.Web
|
||||
import semmle.code.csharp.frameworks.Format
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
|
||||
/**
|
||||
* A data flow source for server side request forgery vulnerabilities.
|
||||
*/
|
||||
abstract private class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for server side request forgery vulnerabilities.
|
||||
*/
|
||||
abstract private class Sink extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* A data flow BarrierGuard which blocks the flow of taint for
|
||||
* server side request forgery vulnerabilities.
|
||||
*/
|
||||
abstract private class BarrierGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A data flow configuration for detecting server side request forgery vulnerabilities.
|
||||
*/
|
||||
class RequestForgeryConfiguration extends DataFlow::Configuration {
|
||||
RequestForgeryConfiguration() { this = "Server Side Request forgery" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
interpolatedStringFlowStep(prev, succ)
|
||||
or
|
||||
stringReplaceStep(prev, succ)
|
||||
or
|
||||
uriCreationStep(prev, succ)
|
||||
or
|
||||
formatConvertStep(prev, succ)
|
||||
or
|
||||
toStringStep(prev, succ)
|
||||
or
|
||||
stringConcatStep(prev, succ)
|
||||
or
|
||||
stringFormatStep(prev, succ)
|
||||
or
|
||||
pathCombineStep(prev, succ)
|
||||
}
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof BarrierGuard
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote data flow source taken as a source
|
||||
* for Server Side Request Forgery(SSRF) Vulnerabilities.
|
||||
*/
|
||||
private class RemoteFlowSourceAsSource extends Source {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* An url argument to a `HttpRequestMessage` constructor call
|
||||
* taken as a sink for Server Side Request Forgery(SSRF) Vulnerabilities.
|
||||
*/
|
||||
private class SystemWebHttpRequestMessageSink extends Sink {
|
||||
SystemWebHttpRequestMessageSink() {
|
||||
exists(Class c | c.hasQualifiedName("System.Net.Http.HttpRequestMessage") |
|
||||
c.getAConstructor().getACall().getArgument(1) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a `WebRequest.Create` call taken as a
|
||||
* sink for Server Side Request Forgery(SSRF) Vulnerabilities. *
|
||||
*/
|
||||
private class SystemNetWebRequestCreateSink extends Sink {
|
||||
SystemNetWebRequestCreateSink() {
|
||||
exists(Method m |
|
||||
m.getDeclaringType().hasQualifiedName("System.Net.WebRequest") and m.hasName("Create")
|
||||
|
|
||||
m.getACall().getArgument(0) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a new HTTP Request call of a `System.Net.Http.HttpClient` object
|
||||
* taken as a sink for Server Side Request Forgery(SSRF) Vulnerabilities.
|
||||
*/
|
||||
private class SystemNetHttpClientSink extends Sink {
|
||||
SystemNetHttpClientSink() {
|
||||
exists(Method m |
|
||||
m.getDeclaringType().hasQualifiedName("System.Net.Http.HttpClient") and
|
||||
m.hasName([
|
||||
"DeleteAsync", "GetAsync", "GetByteArrayAsync", "GetStreamAsync", "GetStringAsync",
|
||||
"PatchAsync", "PostAsync", "PutAsync"
|
||||
])
|
||||
|
|
||||
m.getACall().getArgument(0) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An url argument to a method call of a `System.Net.WebClient` object
|
||||
* taken as a sink for Server Side Request Forgery(SSRF) Vulnerabilities.
|
||||
*/
|
||||
private class SystemNetClientBaseAddressSink extends Sink {
|
||||
SystemNetClientBaseAddressSink() {
|
||||
exists(Property p |
|
||||
p.hasName("BaseAddress") and
|
||||
p.getDeclaringType()
|
||||
.hasQualifiedName(["System.Net.WebClient", "System.Net.Http.HttpClient"])
|
||||
|
|
||||
p.getAnAssignedValue() = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method call which checks the base of the tainted uri is assumed
|
||||
* to be a guard for Server Side Request Forgery(SSRF) Vulnerabilities.
|
||||
* This guard considers all checks as valid.
|
||||
*/
|
||||
private class BaseUriGuard extends BarrierGuard, MethodCall {
|
||||
BaseUriGuard() { this.getTarget().hasQualifiedName("System.Uri", "IsBaseOf") }
|
||||
|
||||
override predicate checks(Expr e, AbstractValue v) {
|
||||
// we consider any checks against the tainted value to sainitize the taint.
|
||||
// This implies any check such as shown below block the taint flow.
|
||||
// Uri url = new Uri("whitelist.com")
|
||||
// if (url.isBaseOf(`taint1))
|
||||
(e = this.getArgument(0) or e = this.getQualifier()) and
|
||||
v.(AbstractValues::BooleanValue).getValue() = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method call which checks if the Uri starts with a white-listed string is assumed
|
||||
* to be a guard for Server Side Request Forgery(SSRF) Vulnerabilities.
|
||||
* This guard considers all checks as valid.
|
||||
*/
|
||||
private class StringStartsWithBarrierGuard extends BarrierGuard, MethodCall {
|
||||
StringStartsWithBarrierGuard() {
|
||||
this.getTarget().hasQualifiedName("System.String", "StartsWith")
|
||||
}
|
||||
|
||||
override predicate checks(Expr e, AbstractValue v) {
|
||||
// Any check such as the ones shown below
|
||||
// "https://myurl.com/".startsWith(`taint`)
|
||||
// `taint`.startsWith("https://myurl.com/")
|
||||
// are assumed to sainitize the taint
|
||||
(e = this.getQualifier() or this.getArgument(0) = e) and
|
||||
v.(AbstractValues::BooleanValue).getValue() = true
|
||||
}
|
||||
}
|
||||
|
||||
private predicate stringFormatStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(FormatCall c | c.getArgument(0) = prev.asExpr() and c = succ.asExpr())
|
||||
}
|
||||
|
||||
private predicate pathCombineStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(MethodCall combineCall |
|
||||
combineCall.getTarget().hasQualifiedName("System.IO.Path", "Combine") and
|
||||
combineCall.getArgument(0) = prev.asExpr() and
|
||||
combineCall = succ.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate uriCreationStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(ObjectCreation oc |
|
||||
oc.getTarget().getDeclaringType().hasQualifiedName("System.Uri") and
|
||||
oc.getArgument(0) = prev.asExpr() and
|
||||
oc = succ.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interpolatedStringFlowStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(InterpolatedStringExpr i |
|
||||
// allow `$"http://{`taint`}/blabla/");"` or
|
||||
// allow `$"https://{`taint`}/blabla/");"`
|
||||
i.getText(0).getValue().matches(["http://", "http://"]) and
|
||||
i.getInsert(1) = prev.asExpr() and
|
||||
succ.asExpr() = i
|
||||
or
|
||||
// allow `$"{`taint`}/blabla/");"`
|
||||
i.getInsert(0) = prev.asExpr() and
|
||||
succ.asExpr() = i
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stringReplaceStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(MethodCall mc, SystemStringClass s |
|
||||
mc = s.getReplaceMethod().getACall() and
|
||||
mc.getQualifier() = prev.asExpr() and
|
||||
succ.asExpr() = mc
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stringConcatStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(AddExpr a |
|
||||
a.getLeftOperand() = prev.asExpr()
|
||||
or
|
||||
a.getRightOperand() = prev.asExpr() and
|
||||
a.getLeftOperand().(StringLiteral).getValue() = ["http://", "https://"]
|
||||
|
|
||||
a = succ.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate formatConvertStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("System.Convert",
|
||||
["FromBase64String", "FromHexString", "FromBase64CharArray"]) and
|
||||
m.getParameter(0) = prev.asParameter() and
|
||||
succ.asExpr() = m.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate toStringStep(DataFlow::Node prev, DataFlow::Node succ) {
|
||||
exists(MethodCall ma |
|
||||
ma.getTarget().hasName("ToString") and
|
||||
ma.getQualifier() = prev.asExpr() and
|
||||
succ.asExpr() = ma
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1083,7 +1083,7 @@ class TranslatedCast extends TranslatedNonConstantExpr {
|
||||
)
|
||||
}
|
||||
|
||||
private TranslatedExpr getOperand() { result = getTranslatedExpr(expr.getExpr()) }
|
||||
private TranslatedExpr getOperand() { result = getTranslatedExpr(expr.(Cast).getExpr()) }
|
||||
|
||||
private Opcode getOpcode() {
|
||||
expr instanceof CastExpr and result instanceof Opcode::CheckedConvertOrThrow
|
||||
@@ -1183,9 +1183,9 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
|
||||
}
|
||||
|
||||
override Opcode getOpcode() {
|
||||
result = binaryArithmeticOpcode(expr) or
|
||||
result = binaryBitwiseOpcode(expr) or
|
||||
result = comparisonOpcode(expr)
|
||||
result = binaryArithmeticOpcode(expr.(BinaryArithmeticOperation)) or
|
||||
result = binaryBitwiseOpcode(expr.(BinaryBitwiseOperation)) or
|
||||
result = comparisonOpcode(expr.(ComparisonOperation))
|
||||
}
|
||||
|
||||
override int getInstructionElementSize(InstructionTag tag) {
|
||||
|
||||
@@ -89,7 +89,7 @@ class TranslatedDeclStmt extends TranslatedStmt {
|
||||
class TranslatedExprStmt extends TranslatedStmt {
|
||||
override ExprStmt stmt;
|
||||
|
||||
TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr()) }
|
||||
TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.(ExprStmt).getExpr()) }
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = this.getExpr() }
|
||||
|
||||
@@ -123,7 +123,7 @@ class TranslatedExprStmtAccessorSet extends TranslatedExprStmt {
|
||||
}
|
||||
|
||||
override TranslatedExpr getExpr() {
|
||||
result = getTranslatedExpr(stmt.getExpr().(AssignExpr).getLValue())
|
||||
result = getTranslatedExpr(stmt.(ExprStmt).getExpr().(AssignExpr).getLValue())
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = this.getExpr() }
|
||||
|
||||
@@ -107,7 +107,7 @@ private predicate impliesValue(
|
||||
wholeIsTrue = true and partIsTrue = true and part = blo.getAnOperand()
|
||||
or
|
||||
wholeIsTrue = true and
|
||||
impliesValue(blo.getAnOperand(), part, partIsTrue, true)
|
||||
impliesValue(blo.getAnOperand().(BinaryLogicalOperation), part, partIsTrue, true)
|
||||
)
|
||||
or
|
||||
blo instanceof LogicalOrExpr and
|
||||
@@ -115,7 +115,7 @@ private predicate impliesValue(
|
||||
wholeIsTrue = false and partIsTrue = false and part = blo.getAnOperand()
|
||||
or
|
||||
wholeIsTrue = false and
|
||||
impliesValue(blo.getAnOperand(), part, partIsTrue, false)
|
||||
impliesValue(blo.getAnOperand().(BinaryLogicalOperation), part, partIsTrue, false)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
|
||||
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
|
||||
exists(boolean partIsTrue, GuardCondition part |
|
||||
impliesValue(this, part, partIsTrue, testIsTrue)
|
||||
impliesValue(this.(BinaryLogicalOperation), part, partIsTrue, testIsTrue)
|
||||
|
|
||||
part.comparesLt(left, right, k, isLessThan, partIsTrue)
|
||||
)
|
||||
@@ -153,7 +153,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
|
||||
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||
exists(boolean partIsTrue, GuardCondition part |
|
||||
impliesValue(this, part, partIsTrue, testIsTrue)
|
||||
impliesValue(this.(BinaryLogicalOperation), part, partIsTrue, testIsTrue)
|
||||
|
|
||||
part.comparesEq(left, right, k, areEqual, partIsTrue)
|
||||
)
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// semmle-extractor-options: ${testdir}/../../resources/stubs/System.Web.cs /r:System.Threading.Tasks.dll /r:System.Collections.Specialized.dll /r:System.Runtime.dll /r:System.Private.Uri.dll
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Mvc;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace RequestForgery.Controllers
|
||||
{
|
||||
public class SSRFController : Controller
|
||||
{
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> Bad(string url)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
|
||||
var client = new HttpClient();
|
||||
await client.SendAsync(request);
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> Good(string url)
|
||||
{
|
||||
string baseUrl = "www.mysecuresite.com/";
|
||||
if (url.StartsWith(baseUrl))
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
var client = new HttpClient();
|
||||
await client.SendAsync(request);
|
||||
|
||||
}
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Missing stubs:
|
||||
namespace System.Net.Http
|
||||
{
|
||||
public class HttpClient
|
||||
{
|
||||
public async Task SendAsync(HttpRequestMessage request) => throw null;
|
||||
}
|
||||
|
||||
public class HttpRequestMessage
|
||||
{
|
||||
public HttpRequestMessage(HttpMethod method, string requestUri) => throw null;
|
||||
}
|
||||
|
||||
public class HttpMethod
|
||||
{
|
||||
public static readonly HttpMethod Get;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
edges
|
||||
| RequestForgery.cs:14:52:14:54 | url : String | RequestForgery.cs:16:66:16:68 | access to parameter url |
|
||||
nodes
|
||||
| RequestForgery.cs:14:52:14:54 | url : String | semmle.label | url : String |
|
||||
| RequestForgery.cs:16:66:16:68 | access to parameter url | semmle.label | access to parameter url |
|
||||
subpaths
|
||||
#select
|
||||
| RequestForgery.cs:16:66:16:68 | access to parameter url | RequestForgery.cs:14:52:14:54 | url : String | RequestForgery.cs:16:66:16:68 | access to parameter url | $@ flows to here and is used in a server side web request. | RequestForgery.cs:14:52:14:54 | url | User-provided value |
|
||||
@@ -1 +0,0 @@
|
||||
experimental/CWE-918/RequestForgery.ql
|
||||
@@ -1333,7 +1333,7 @@ The values of a set literal expression are all the values of all the contained e
|
||||
|
||||
Set literals are supported from release 2.1.0 of the CodeQL CLI, and release 1.24 of LGTM Enterprise.
|
||||
|
||||
Since release 2.7.1 of the CodeQL CLI, and release 1.30 of LGTM Enterprise, a trailing comma is allowed in a set literal.
|
||||
Since release 2.7.1 of the CodeQL CLI, and release 1.28 of LGTM Enterprise, a trailing comma is allowed in a set literal.
|
||||
|
||||
Disambiguation of expressions
|
||||
-----------------------------
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
`GitHub CodeQL Terms and Conditions <https://securitylab.github.com/tools/codeql/license>`__.
|
||||
|
||||
GitHub CodeQL is licensed on a per-user basis. Under the license restrictions,
|
||||
you can use CodeQL to perform the following tasks:
|
||||
you can CodeQL to perform the following tasks:
|
||||
|
||||
- To perform academic research.
|
||||
- To demonstrate the software.
|
||||
|
||||
@@ -155,26 +155,20 @@ Python built-in support
|
||||
Name, Category
|
||||
aiohttp.web, Web framework
|
||||
Django, Web framework
|
||||
FastAPI, Web framework
|
||||
Flask, Web framework
|
||||
Tornado, Web framework
|
||||
Twisted, Web framework
|
||||
starlette, Asynchronous Server Gateway Interface (ASGI)
|
||||
dill, Serialization
|
||||
PyYAML, Serialization
|
||||
ruamel.yaml, Serialization
|
||||
dill, Serialization
|
||||
simplejson, Serialization
|
||||
toml, Serialization
|
||||
ujson, Serialization
|
||||
fabric, Utility library
|
||||
idna, Utility library
|
||||
invoke, Utility library
|
||||
jmespath, Utility library
|
||||
multidict, Utility library
|
||||
pydantic, Utility library
|
||||
yarl, Utility library
|
||||
aioch, Database
|
||||
asyncpg, Database
|
||||
clickhouse-driver, Database
|
||||
mysql-connector-python, Database
|
||||
mysql-connector, Database
|
||||
|
||||
@@ -150,19 +150,14 @@ call-graph should be defined as a predicate:
|
||||
```ql
|
||||
DataFlowCallable viableCallable(DataFlowCall c)
|
||||
```
|
||||
Furthermore, each `Node` must be associated with exactly one callable and this
|
||||
relation should be defined as:
|
||||
```ql
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n)
|
||||
```
|
||||
|
||||
In order to connect data-flow across calls, the 4 `Node` subclasses
|
||||
`ArgumentNode`, `ParameterNode`, `ReturnNode`, and `OutNode` are used.
|
||||
Flow into callables from arguments to parameters are matched up using an
|
||||
integer position, so these two predicates must be defined:
|
||||
integer position, so these two classes must define:
|
||||
```ql
|
||||
ArgumentNode::argumentOf(DataFlowCall call, int pos)
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos)
|
||||
ParameterNode::isParameterOf(DataFlowCallable c, int pos)
|
||||
```
|
||||
It is typical to use `pos = -1` for an implicit `this`-parameter.
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The predicate `StringLiteral.getRepresentedString()` has been deprecated for removal in a future version because it is just an alias for `getValue()`. That predicate should be used instead.
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* Added data flow models for lambda methods on `java.util.Optional`.
|
||||
@@ -9,7 +9,7 @@ cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.esotericsoftware.kryo5.io,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.fasterxml.jackson.core,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
com.fasterxml.jackson.databind,,,6,,,,,,,,,,,,,,,,,,,,,6,
|
||||
com.fasterxml.jackson.databind,,,5,,,,,,,,,,,,,,,,,,,,,5,
|
||||
com.google.common.base,,,85,,,,,,,,,,,,,,,,,,,,,62,23
|
||||
com.google.common.cache,,,17,,,,,,,,,,,,,,,,,,,,,,17
|
||||
com.google.common.collect,,,553,,,,,,,,,,,,,,,,,,,,,2,551
|
||||
@@ -30,7 +30,7 @@ java.lang,,,51,,,,,,,,,,,,,,,,,,,,,41,10
|
||||
java.net,10,3,7,,,,,,,,,,,10,,,,,,,,,3,7,
|
||||
java.nio,10,,4,,10,,,,,,,,,,,,,,,,,,,4,
|
||||
java.sql,7,,,,,,,,,,,,,,,7,,,,,,,,,
|
||||
java.util,,,429,,,,,,,,,,,,,,,,,,,,,15,414
|
||||
java.util,,,420,,,,,,,,,,,,,,,,,,,,,15,405
|
||||
javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,2,,7,,
|
||||
javax.json,,,123,,,,,,,,,,,,,,,,,,,,,100,23
|
||||
javax.management.remote,2,,,,,,,,,2,,,,,,,,,,,,,,,
|
||||
@@ -90,12 +90,3 @@ org.springframework.web.util,,,163,,,,,,,,,,,,,,,,,,,,,138,25
|
||||
org.xml.sax,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.xmlpull.v1,,3,,,,,,,,,,,,,,,,,,,,,3,,
|
||||
play.mvc,,4,,,,,,,,,,,,,,,,,,,,,4,,
|
||||
ratpack.core.form,,,3,,,,,,,,,,,,,,,,,,,,,3,
|
||||
ratpack.core.handling,,6,4,,,,,,,,,,,,,,,,,,,,6,4,
|
||||
ratpack.core.http,,10,10,,,,,,,,,,,,,,,,,,,,10,10,
|
||||
ratpack.exec,,,26,,,,,,,,,,,,,,,,,,,,,,26
|
||||
ratpack.form,,,3,,,,,,,,,,,,,,,,,,,,,3,
|
||||
ratpack.func,,,5,,,,,,,,,,,,,,,,,,,,,,5
|
||||
ratpack.handling,,6,4,,,,,,,,,,,,,,,,,,,,6,4,
|
||||
ratpack.http,,10,10,,,,,,,,,,,,,,,,,,,,10,10,
|
||||
ratpack.util,,,5,,,,,,,,,,,,,,,,,,,,,,5
|
||||
|
||||
|
@@ -15,9 +15,9 @@ Java framework & library support
|
||||
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,728,6,,6,,,,,
|
||||
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,,
|
||||
Java Standard Library,``java.*``,3,519,30,13,,,7,,,10
|
||||
Java Standard Library,``java.*``,3,510,30,13,,,7,,,10
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",54,552,32,,,4,,1,1,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,469,91,,,,19,14,,29
|
||||
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``",39,99,151,,,,14,18,,
|
||||
Totals,,175,5341,408,13,6,10,107,33,1,66
|
||||
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``",7,28,151,,,,14,18,,
|
||||
Totals,,143,5261,408,13,6,10,107,33,1,66
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ private module ControlFlowGraphImpl {
|
||||
* `TypeThrowable` which results in both `TypeError` and `TypeRuntimeException`.
|
||||
*/
|
||||
UncheckedThrowableType getAnUncheckedSubtype() {
|
||||
result = this
|
||||
result = this.(UncheckedThrowableType)
|
||||
or
|
||||
result instanceof TypeError and this instanceof TypeThrowable
|
||||
or
|
||||
|
||||
@@ -166,7 +166,7 @@ class CompileTimeConstantExpr extends Expr {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
string getStringValue() {
|
||||
result = this.(StringLiteral).getValue()
|
||||
result = this.(StringLiteral).getRepresentedString()
|
||||
or
|
||||
result =
|
||||
this.(AddExpr).getLeftOperand().(CompileTimeConstantExpr).getStringValue() +
|
||||
@@ -298,15 +298,18 @@ class CompileTimeConstantExpr extends Expr {
|
||||
*
|
||||
* Note that this does not handle the following cases:
|
||||
*
|
||||
* - values of type `long`.
|
||||
* - values of type `long`,
|
||||
* - `char` literals.
|
||||
*/
|
||||
cached
|
||||
int getIntValue() {
|
||||
exists(IntegralType t | this.getType() = t | t.getName().toLowerCase() != "long") and
|
||||
(
|
||||
result = this.(IntegerLiteral).getIntValue()
|
||||
or
|
||||
result = this.(CharacterLiteral).getCodePointValue()
|
||||
exists(string lit | lit = this.(Literal).getValue() |
|
||||
// `char` literals may get parsed incorrectly, so disallow.
|
||||
not this instanceof CharacterLiteral and
|
||||
result = lit.toInt()
|
||||
)
|
||||
or
|
||||
exists(CastExpr cast, int val |
|
||||
cast = this and val = cast.getExpr().(CompileTimeConstantExpr).getIntValue()
|
||||
@@ -716,22 +719,6 @@ class DoubleLiteral extends Literal, @doubleliteral {
|
||||
/** A character literal. For example, `'\n'`. */
|
||||
class CharacterLiteral extends Literal, @characterliteral {
|
||||
override string getAPrimaryQlClass() { result = "CharacterLiteral" }
|
||||
|
||||
/**
|
||||
* Gets a string which consists of the single character represented by
|
||||
* this literal.
|
||||
*
|
||||
* Unicode surrogate characters (U+D800 to U+DFFF) have the replacement character
|
||||
* U+FFFD as result instead.
|
||||
*/
|
||||
override string getValue() { result = super.getValue() }
|
||||
|
||||
/**
|
||||
* Gets the Unicode code point value of the character represented by
|
||||
* this literal. The result is the same as if the Java code had cast
|
||||
* the character to an `int`.
|
||||
*/
|
||||
int getCodePointValue() { result.toUnicode() = this.getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -745,21 +732,9 @@ class CharacterLiteral extends Literal, @characterliteral {
|
||||
*/
|
||||
class StringLiteral extends Literal, @stringliteral {
|
||||
/**
|
||||
* Gets the string represented by this string literal, that is, the content
|
||||
* of the literal without enclosing quotes and with escape sequences translated.
|
||||
*
|
||||
* Unpaired Unicode surrogate characters (U+D800 to U+DFFF) are replaced with the
|
||||
* replacement character U+FFFD.
|
||||
*/
|
||||
override string getValue() { result = super.getValue() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: This predicate will be removed in a future version because
|
||||
* it is just an alias for `getValue()`; that predicate should be used instead.
|
||||
*
|
||||
* Gets the literal string without the quotes.
|
||||
*/
|
||||
deprecated string getRepresentedString() { result = this.getValue() }
|
||||
string getRepresentedString() { result = this.getValue() }
|
||||
|
||||
/** Holds if this string literal is a text block (`""" ... """`). */
|
||||
predicate isTextBlock() { this.getLiteral().matches("\"\"\"%") }
|
||||
@@ -1391,11 +1366,15 @@ class InstanceOfExpr extends Expr, @instanceofexpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 14. Subject to removal in a future release.
|
||||
*
|
||||
* Holds if this `instanceof` expression uses pattern matching.
|
||||
*/
|
||||
predicate isPattern() { exists(this.getLocalVariableDeclExpr()) }
|
||||
|
||||
/**
|
||||
* PREVIEW FEATURE in Java 14. Subject to removal in a future release.
|
||||
*
|
||||
* Gets the local variable declaration of this `instanceof` expression if pattern matching is used.
|
||||
*/
|
||||
LocalVariableDeclExpr getLocalVariableDeclExpr() { result.isNthChildOf(this, 0) }
|
||||
|
||||
@@ -25,7 +25,9 @@ class SuppressWarningsAnnotation extends Annotation {
|
||||
}
|
||||
|
||||
/** Gets the name of a warning suppressed by this annotation. */
|
||||
string getASuppressedWarning() { result = this.getASuppressedWarningLiteral().getValue() }
|
||||
string getASuppressedWarning() {
|
||||
result = this.getASuppressedWarningLiteral().getRepresentedString()
|
||||
}
|
||||
}
|
||||
|
||||
/** A `@Target` annotation. */
|
||||
|
||||
@@ -665,7 +665,7 @@ final class GenericTypeNode extends PrintAstNode, TGenericTypeNode {
|
||||
override Location getLocation() { none() }
|
||||
|
||||
override ElementNode getChild(int childIndex) {
|
||||
result.getElement() = ty.getTypeParameter(childIndex)
|
||||
result.getElement().(TypeVariable) = ty.getTypeParameter(childIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,7 +686,7 @@ final class GenericCallableNode extends PrintAstNode, TGenericCallableNode {
|
||||
override string toString() { result = "(Generic Parameters)" }
|
||||
|
||||
override ElementNode getChild(int childIndex) {
|
||||
result.getElement() = c.getTypeParameter(childIndex)
|
||||
result.getElement().(TypeVariable) = c.getTypeParameter(childIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,7 +75,7 @@ class ReflectiveClassIdentifierMethodAccess extends ReflectiveClassIdentifier, M
|
||||
/**
|
||||
* If the argument to this call is a `StringLiteral`, then return that string.
|
||||
*/
|
||||
string getTypeName() { result = this.getArgument(0).(StringLiteral).getValue() }
|
||||
string getTypeName() { result = this.getArgument(0).(StringLiteral).getRepresentedString() }
|
||||
|
||||
override RefType getReflectivelyIdentifiedClass() {
|
||||
// We only handle cases where the class is specified as a string literal to this call.
|
||||
@@ -150,7 +150,7 @@ private Type parameterForSubTypes(ParameterizedType type) {
|
||||
lowerBound = arg.(Wildcard).getLowerBoundType()
|
||||
|
|
||||
// `T super Foo` implies that `Foo`, or any super-type of `Foo`, may be represented.
|
||||
lowerBound.getAnAncestor() = result
|
||||
lowerBound.(RefType).getAnAncestor() = result
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -360,7 +360,7 @@ class ReflectiveMethodAccess extends ClassMethodAccess {
|
||||
this.getInferredClassType().inherits(result)
|
||||
) and
|
||||
// Only consider instances where the method name is provided as a `StringLiteral`.
|
||||
result.hasName(this.getArgument(0).(StringLiteral).getValue())
|
||||
result.hasName(this.getArgument(0).(StringLiteral).getRepresentedString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,6 +400,6 @@ class ReflectiveFieldAccess extends ClassMethodAccess {
|
||||
this.getInferredClassType().inherits(result)
|
||||
)
|
||||
) and
|
||||
result.hasName(this.getArgument(0).(StringLiteral).getValue())
|
||||
result.hasName(this.getArgument(0).(StringLiteral).getRepresentedString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +567,7 @@ class ThrowStmt extends Stmt, @throwstmt {
|
||||
or
|
||||
exists(Stmt mid |
|
||||
mid = this.findEnclosing() and
|
||||
not exists(this.catchClauseForThis(mid)) and
|
||||
not exists(this.catchClauseForThis(mid.(TryStmt))) and
|
||||
result = mid.getEnclosingStmt()
|
||||
)
|
||||
}
|
||||
@@ -575,7 +575,7 @@ class ThrowStmt extends Stmt, @throwstmt {
|
||||
private CatchClause catchClauseForThis(TryStmt try) {
|
||||
result = try.getACatchClause() and
|
||||
result.getEnclosingCallable() = this.getEnclosingCallable() and
|
||||
this.getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType()) and
|
||||
this.getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType().(RefType)) and
|
||||
not this.getEnclosingStmt+() = result
|
||||
}
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ private predicate formatStringFragment(Expr fmt) {
|
||||
private predicate formatStringValue(Expr e, string fmtvalue) {
|
||||
formatStringFragment(e) and
|
||||
(
|
||||
e.(StringLiteral).getValue() = fmtvalue
|
||||
e.(StringLiteral).getRepresentedString() = fmtvalue
|
||||
or
|
||||
e.getType() instanceof IntegralType and fmtvalue = "1" // dummy value
|
||||
or
|
||||
@@ -318,7 +318,7 @@ private predicate formatStringValue(Expr e, string fmtvalue) {
|
||||
getprop.hasName("getProperty") and
|
||||
getprop.getDeclaringType().hasQualifiedName("java.lang", "System") and
|
||||
getprop.getNumberOfParameters() = 1 and
|
||||
ma.getAnArgument().(StringLiteral).getValue() = prop and
|
||||
ma.getAnArgument().(StringLiteral).getRepresentedString() = prop and
|
||||
(prop = "line.separator" or prop = "file.separator" or prop = "path.separator") and
|
||||
fmtvalue = "x" // dummy value
|
||||
)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user