Compare commits

..

1 Commits

Author SHA1 Message Date
Nick Rolfe
11bcad37af Ruby: update crate versions 2021-10-27 18:12:12 +01:00
326 changed files with 3241 additions and 7481 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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() + ")"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>about General Class-Level Information</p>
<!--TOC-->
</overview>
</qhelp>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>about Architecture</p>
<!--TOC-->
</overview>
</qhelp>

View File

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

View 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

View File

@@ -0,0 +1,16 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>about best practices</p>
<!--TOC-->
</overview>
</qhelp>

View File

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

View File

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

View File

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

View File

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

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

View File

@@ -0,0 +1,6 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="HChurn.qhelp" />
</qhelp>

View File

@@ -0,0 +1,6 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="HChurn.qhelp" />
</qhelp>

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +0,0 @@
experimental/CWE-918/RequestForgery.ql

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Added data flow models for lambda methods on `java.util.Optional`.

View File

@@ -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
1 package sink source summary sink:bean-validation sink:create-file sink:groovy sink:header-splitting sink:information-leak sink:jexl sink:jndi-injection sink:ldap sink:mvel sink:ognl-injection sink:open-url sink:set-hostname-verifier sink:sql sink:url-open-stream sink:url-redirect sink:xpath sink:xslt sink:xss source:contentprovider source:remote summary:taint summary:value
9 com.esotericsoftware.kryo.io 1 1
10 com.esotericsoftware.kryo5.io 1 1
11 com.fasterxml.jackson.core 1 1
12 com.fasterxml.jackson.databind 6 5 6 5
13 com.google.common.base 85 62 23
14 com.google.common.cache 17 17
15 com.google.common.collect 553 2 551
30 java.net 10 3 7 10 3 7
31 java.nio 10 4 10 4
32 java.sql 7 7
33 java.util 429 420 15 414 405
34 javax.faces.context 2 7 2 7
35 javax.json 123 100 23
36 javax.management.remote 2 2
90 org.xml.sax 1 1
91 org.xmlpull.v1 3 3
92 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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