Merge branch 'main' into redsun82/update-rules-rust

This commit is contained in:
Paolo Tranquilli
2026-03-19 08:36:31 +01:00
committed by GitHub
260 changed files with 2797 additions and 3276 deletions

View File

@@ -199,6 +199,7 @@ def annotate_as_appropriate(filename, lines):
# as overlay[local?]. It is not clear that these heuristics are exactly what we want,
# but they seem to work well enough for now (as determined by speed and accuracy numbers).
if (filename.endswith("Test.qll") or
re.search(r"go/ql/lib/semmle/go/security/[^/]+[.]qll$", filename.replace(os.sep, "/")) or
((filename.endswith("Query.qll") or filename.endswith("Config.qll")) and
any("implements DataFlow::ConfigSig" in line for line in lines))):
return None

View File

@@ -172,10 +172,6 @@
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
],
"C# ControlFlowReachability": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"

View File

@@ -524,6 +524,12 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
not exists(NewOrNewArrayExpr new | e = new.getAllocatorCall().getArgument(0))
)
}
/**
* Holds if this function has an ambiguous return type, meaning that zero or multiple return
* types for this function are present in the database (this can occur in `build-mode: none`).
*/
predicate hasAmbiguousReturnType() { count(this.getType()) != 1 }
}
pragma[noinline]

View File

@@ -6,117 +6,67 @@ private import OverlayXml
/**
* Holds always for the overlay variant and never for the base variant.
* This local predicate is used to define local predicates that behave
* differently for the base and overlay variant.
*/
overlay[local]
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
overlay[local]
private string getLocationFilePath(@location_default loc) {
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
}
/**
* Gets the file path for an element with a single location.
* Holds if the TRAP file or tag `t` is reachable from source file `sourceFile`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
*/
overlay[local]
private string getSingleLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(e, _, _, _, loc)
or
fun_decls(e, _, _, _, loc)
or
type_decls(e, _, loc)
or
namespace_decls(e, _, loc, _)
or
macroinvocations(e, _, loc, _)
or
preprocdirects(e, _, loc)
or
diagnostics(e, _, _, _, _, loc)
or
usings(e, _, loc, _)
or
static_asserts(e, _, _, loc, _)
or
derivations(e, _, _, _, loc)
or
frienddecls(e, _, _, loc)
or
comments(e, _, loc)
or
exprs(e, _, loc)
or
stmts(e, _, loc)
or
initialisers(e, _, _, loc)
or
attributes(e, _, _, _, loc)
or
attribute_args(e, _, _, _, loc)
or
namequalifiers(e, _, _, loc)
or
enumconstants(e, _, _, _, _, loc)
or
type_mentions(e, _, loc, _)
or
lambda_capture(e, _, _, _, _, _, loc)
or
concept_templates(e, _, loc)
|
result = getLocationFilePath(loc)
private predicate locallyReachableTrapOrTag(
boolean isOverlayVariant, string sourceFile, @trap_or_tag t
) {
exists(@source_file sf, @trap trap |
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
source_file_uses_trap(sf, trap) and
source_file_name(sf, sourceFile) and
(t = trap or trap_uses_tag(trap, t))
)
}
/**
* Gets the file path for an element with potentially multiple locations.
* Holds if element `e` is in TRAP file or tag `t`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
*/
overlay[local]
private string getMultiLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(_, e, _, _, loc)
or
fun_decls(_, e, _, _, loc)
or
type_decls(_, e, loc)
or
namespace_decls(_, e, loc, _)
|
result = getLocationFilePath(loc)
)
}
/**
* A local helper predicate that holds in the base variant and never in the
* overlay variant.
*/
overlay[local]
private predicate isBase() { not isOverlay() }
/**
* Holds if `path` was extracted in the overlay database.
*/
overlay[local]
private predicate overlayHasFile(string path) {
isOverlay() and
files(_, path) and
path != ""
private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) {
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
in_trap_or_tag(e, t)
}
/**
* Discards an element from the base variant if:
* - It has a single location in a file extracted in the overlay, or
* - All of its locations are in files extracted in the overlay.
* - We have knowledge about what TRAP file or tag it is in (in the base).
* - It is not in any overlay TRAP file or tag that is reachable from an overlay source file.
* - For every base TRAP file or tag that contains it and is reachable from a base source file,
* either the source file has changed, or the overlay has redefined the TRAP file or tag,
* or the overlay runner has re-extracted the same source file.
*/
overlay[discard_entity]
private predicate discardElement(@element e) {
isBase() and
(
overlayHasFile(getSingleLocationFilePath(e))
or
forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path))
// If we don't have any knowledge about what TRAP file something
// is in, then we don't want to discard it, so we only consider
// entities that are known to be in a base TRAP file or tag.
locallyInTrapOrTag(false, e, _) and
// Anything that is reachable from an overlay source file should
// not be discarded.
not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) |
locallyReachableTrapOrTag(true, _, t)
) and
// Finally, we have to make sure the base variant does not retain it.
// If it is reachable from a base source file, then that is
// sufficient unless either the base source file has changed (in
// particular, been deleted), or the overlay has redefined the TRAP
// file or tag it is in, or the overlay runner has re-extracted the same
// source file (e.g. because a header it includes has changed).
forall(@trap_or_tag t, string sourceFile |
locallyInTrapOrTag(false, e, t) and
locallyReachableTrapOrTag(false, sourceFile, t)
|
overlayChangedFiles(sourceFile) or
locallyReachableTrapOrTag(true, _, t) or
locallyReachableTrapOrTag(true, sourceFile, _)
)
}

View File

@@ -218,7 +218,9 @@ where
// only report if we cannot prove that the result of the
// multiplication will be less (resp. greater) than the
// maximum (resp. minimum) number we can compute.
overflows(me, t1)
overflows(me, t1) and
// exclude cases where the expression type may not have been extracted accurately
not me.getParent().(Call).getTarget().hasAmbiguousReturnType()
select me,
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
+ me.getFullyConverted().getType().toString() + "'."

View File

@@ -4,7 +4,7 @@
* allows for a cross-site scripting vulnerability.
* @kind path-problem
* @problem.severity error
* @security-severity 6.1
* @security-severity 7.8
* @precision high
* @id cpp/cgi-xss
* @tags security

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Fixed an issue with the "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query causing false positive results in `build-mode: none` databases.

View File

@@ -0,0 +1,4 @@
---
category: queryMetadata
---
* The `@security-severity` metadata of `cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high).

View File

@@ -0,0 +1,28 @@
// semmle-extractor-options: --expect_errors
void test_float_double1(float f, double d) {
float r1 = f * f; // GOOD
float r2 = f * d; // GOOD
double r3 = f * f; // BAD
double r4 = f * d; // GOOD
float f1 = fabsf(f * f); // GOOD
float f2 = fabsf(f * d); // GOOD
double f3 = fabs(f * f); // BAD [NOT DETECTED]
double f4 = fabs(f * d); // GOOD
}
double fabs(double f);
float fabsf(float f);
void test_float_double2(float f, double d) {
float r1 = f * f; // GOOD
float r2 = f * d; // GOOD
double r3 = f * f; // BAD
double r4 = f * d; // GOOD
float f1 = fabsf(f * f); // GOOD
float f2 = fabsf(f * d); // GOOD
double f3 = fabs(f * f); // BAD [NOT DETECTED]
double f4 = fabs(f * d); // GOOD
}

View File

@@ -1,3 +1,5 @@
| Buildless.c:6:17:6:21 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |
| Buildless.c:21:17:21:21 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |
| IntMultToLong.c:4:10:4:14 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
| IntMultToLong.c:7:16:7:20 | ... * ... | Multiplication result may overflow 'int' before it is converted to 'long long'. |
| IntMultToLong.c:18:19:18:23 | ... * ... | Multiplication result may overflow 'float' before it is converted to 'double'. |

View File

@@ -1,63 +1,5 @@
import csharp
import semmle.code.csharp.controlflow.internal.Completion
import semmle.code.csharp.controlflow.internal.PreBasicBlocks
import ControlFlow
import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::Consistency
import semmle.code.csharp.controlflow.internal.Splitting
private predicate splitBB(ControlFlow::BasicBlock bb) {
exists(ControlFlow::Node first |
first = bb.getFirstNode() and
first.isJoin() and
strictcount(first.getAPredecessor().getAstNode()) = 1
)
}
private class RelevantBasicBlock extends ControlFlow::BasicBlock {
RelevantBasicBlock() { not splitBB(this) }
}
predicate bbStartInconsistency(ControlFlowElement cfe) {
exists(RelevantBasicBlock bb | bb.getFirstNode() = cfe.getAControlFlowNode()) and
not cfe = any(PreBasicBlock bb).getFirstElement()
}
predicate bbSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
exists(RelevantBasicBlock predBB, RelevantBasicBlock succBB |
predBB.getLastNode() = pred.getAControlFlowNode() and
succBB = predBB.getASuccessor() and
succBB.getFirstNode() = succ.getAControlFlowNode()
) and
not exists(PreBasicBlock predBB, PreBasicBlock succBB |
predBB.getLastNode() = pred and
succBB = predBB.getASuccessor() and
succBB.getFirstElement() = succ
)
}
predicate bbIntraSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
exists(ControlFlow::BasicBlock bb, int i |
pred.getAControlFlowNode() = bb.getNode(i) and
succ.getAControlFlowNode() = bb.getNode(i + 1)
) and
not exists(PreBasicBlock bb |
bb.getLastNode() = pred and
bb.getASuccessor().getFirstElement() = succ
) and
not exists(PreBasicBlock bb, int i |
bb.getNode(i) = pred and
bb.getNode(i + 1) = succ
)
}
query predicate preBasicBlockConsistency(ControlFlowElement cfe1, ControlFlowElement cfe2, string s) {
bbStartInconsistency(cfe1) and
cfe2 = cfe1 and
s = "start inconsistency"
or
bbSuccInconsistency(cfe1, cfe2) and
s = "succ inconsistency"
or
bbIntraSuccInconsistency(cfe1, cfe2) and
s = "intra succ inconsistency"
}

View File

@@ -35,9 +35,7 @@ private module Input implements InputSig<Location, CsharpDataFlow> {
or
n.asExpr().(ObjectCreation).hasInitializer()
or
exists(
n.(PostUpdateNode).getPreUpdateNode().asExprAtNode(LocalFlow::getPostUpdateReverseStep(_))
)
n.(PostUpdateNode).getPreUpdateNode().asExpr() = LocalFlow::getPostUpdateReverseStep(_)
}
predicate argHasPostUpdateExclude(ArgumentNode n) {

View File

@@ -336,6 +336,22 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl {
ExtensionTypeExtensionMethod() { this.isInExtension() }
}
/**
* A non-static member with an initializer, for example a field `int Field = 0`.
*/
private class InitializedInstanceMember extends Member {
private AssignExpr ae;
InitializedInstanceMember() {
not this.isStatic() and
expr_parent_top_level(ae, _, this) and
not ae = getExpressionBody(_)
}
/** Gets the initializer expression. */
AssignExpr getInitializer() { result = ae }
}
/**
* An object initializer method.
*
@@ -347,6 +363,17 @@ class ExtensionTypeExtensionMethod extends ExtensionMethodImpl {
*/
class ObjectInitMethod extends Method {
ObjectInitMethod() { this.getName() = "<object initializer>" }
/**
* Holds if this object initializer method performs the initialization
* of a member via assignment `init`.
*/
predicate initializes(AssignExpr init) {
exists(InitializedInstanceMember m |
this.getDeclaringType().getAMember() = m and
init = m.getInitializer()
)
}
}
/**

View File

@@ -214,6 +214,8 @@ private module Cached {
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
or
parent*(cfe, c.(Constructor).getObjectInitializerCall())
or
parent*(cfe, any(AssignExpr init | c.(ObjectInitMethod).initializes(init)))
}
/** Holds if the enclosing statement of expression `e` is `s`. */

View File

@@ -10,42 +10,15 @@ private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.commons.Compilation
private module Initializers {
/**
* A non-static member with an initializer, for example a field `int Field = 0`.
*/
class InitializedInstanceMember extends Member {
private AssignExpr ae;
InitializedInstanceMember() {
not this.isStatic() and
expr_parent_top_level(ae, _, this) and
not ae = any(Callable c).getExpressionBody()
}
/** Gets the initializer expression. */
AssignExpr getInitializer() { result = ae }
}
/**
* Holds if `obinit` is an object initializer method that performs the initialization
* of a member via assignment `init`.
*/
predicate obinitInitializes(ObjectInitMethod obinit, AssignExpr init) {
exists(InitializedInstanceMember m |
obinit.getDeclaringType().getAMember() = m and
init = m.getInitializer()
)
}
/**
* Gets the `i`th member initializer expression for object initializer method `obinit`
* in compilation `comp`.
*/
AssignExpr initializedInstanceMemberOrder(ObjectInitMethod obinit, CompilationExt comp, int i) {
obinitInitializes(obinit, result) and
obinit.initializes(result) and
result =
rank[i + 1](AssignExpr ae0, Location l |
obinitInitializes(obinit, ae0) and
obinit.initializes(ae0) and
l = ae0.getLocation() and
getCompilation(l.getFile()) = comp
|
@@ -74,7 +47,7 @@ class CfgScope extends Element, @top_level_exprorstmt_parent {
any(Callable c |
c.(Constructor).hasInitializer()
or
Initializers::obinitInitializes(c, _)
c.(ObjectInitMethod).initializes(_)
or
c.hasBody()
)

View File

@@ -1,175 +0,0 @@
/**
* INTERNAL: Do not use.
*
* Provides a basic block implementation on control flow elements. That is,
* a "pre-CFG" where the nodes are (unsplit) control flow elements and the
* successor relation is `succ = succ(pred, _)`.
*
* The logic is duplicated from the implementation in `BasicBlocks.qll`, and
* being an internal class, all predicate documentation has been removed.
*/
import csharp
private import Completion
private import ControlFlowGraphImpl
private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg
private import codeql.controlflow.BasicBlock as BB
private predicate startsBB(ControlFlowElement cfe) {
not succ(_, cfe, _) and
(
succ(cfe, _, _)
or
scopeLast(_, cfe, _)
)
or
strictcount(ControlFlowElement pred, Completion c | succ(pred, cfe, c)) > 1
or
succ(_, cfe, any(ConditionalCompletion c))
or
exists(ControlFlowElement pred, int i |
succ(pred, cfe, _) and
i = count(ControlFlowElement succ, Completion c | succ(pred, succ, c))
|
i > 1
or
i = 1 and
scopeLast(_, pred, _)
)
}
private predicate intraBBSucc(ControlFlowElement pred, ControlFlowElement succ) {
succ(pred, succ, _) and
not startsBB(succ)
}
private predicate bbIndex(ControlFlowElement bbStart, ControlFlowElement cfe, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfe, i)
private predicate succBB(PreBasicBlock pred, PreBasicBlock succ) { succ = pred.getASuccessor() }
private predicate entryBB(PreBasicBlock bb) { scopeFirst(_, bb) }
private predicate bbIDominates(PreBasicBlock dom, PreBasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
class PreBasicBlock extends ControlFlowElement {
PreBasicBlock() { startsBB(this) }
PreBasicBlock getASuccessor(Cfg::SuccessorType t) {
succ(this.getLastNode(), result, any(Completion c | t = c.getAMatchingSuccessorType()))
}
deprecated PreBasicBlock getASuccessorByType(Cfg::SuccessorType t) {
result = this.getASuccessor(t)
}
PreBasicBlock getASuccessor() { result = this.getASuccessor(_) }
PreBasicBlock getAPredecessor() { result.getASuccessor() = this }
ControlFlowElement getNode(int pos) { bbIndex(this, result, pos) }
deprecated ControlFlowElement getElement(int pos) { result = this.getNode(pos) }
ControlFlowElement getAnElement() { result = this.getNode(_) }
ControlFlowElement getFirstElement() { result = this }
ControlFlowElement getLastNode() { result = this.getNode(this.length() - 1) }
deprecated ControlFlowElement getLastElement() { result = this.getLastNode() }
int length() { result = strictcount(this.getAnElement()) }
PreBasicBlock getImmediateDominator() { bbIDominates(result, this) }
predicate immediatelyDominates(PreBasicBlock bb) { bbIDominates(this, bb) }
pragma[inline]
predicate strictlyDominates(PreBasicBlock bb) { this.immediatelyDominates+(bb) }
pragma[inline]
predicate dominates(PreBasicBlock bb) {
bb = this
or
this.strictlyDominates(bb)
}
predicate inDominanceFrontier(PreBasicBlock df) {
this = df.getAPredecessor() and not bbIDominates(this, df)
or
exists(PreBasicBlock prev | prev.inDominanceFrontier(df) |
bbIDominates(this, prev) and
not bbIDominates(this, df)
)
}
/** Unsupported. Do not use. */
predicate strictlyPostDominates(PreBasicBlock bb) { none() }
/** Unsupported. Do not use. */
predicate postDominates(PreBasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
}
private Completion getConditionalCompletion(ConditionalCompletion cc) {
result.getInnerCompletion() = cc
}
pragma[nomagic]
private predicate conditionBlockImmediatelyControls(
ConditionBlock cond, PreBasicBlock succ, ConditionalCompletion cc
) {
exists(ControlFlowElement last, Completion c |
last = cond.getLastNode() and
c = getConditionalCompletion(cc) and
succ(last, succ, c) and
// In the pre-CFG, we need to account for case where one predecessor node has
// two edges to the same successor node. Assertion expressions are examples of
// such nodes.
not exists(Completion other |
succ(last, succ, other) and
other != c
) and
forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != cond |
succ.dominates(pred)
)
)
}
class ConditionBlock extends PreBasicBlock {
ConditionBlock() {
exists(Completion c | c = getConditionalCompletion(_) |
succ(this.getLastNode(), _, c)
or
scopeLast(_, this.getLastNode(), c)
)
}
pragma[nomagic]
predicate controls(PreBasicBlock controlled, Cfg::ConditionalSuccessor s) {
exists(PreBasicBlock succ, ConditionalCompletion c |
conditionBlockImmediatelyControls(this, succ, c)
|
succ.dominates(controlled) and
s = c.getAMatchingSuccessorType()
)
}
}
module PreCfg implements BB::CfgSig<Location> {
class ControlFlowNode = ControlFlowElement;
class BasicBlock = PreBasicBlock;
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
}
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
conditionBlockImmediatelyControls(bb1, bb2, _)
}
}

View File

@@ -1,246 +0,0 @@
import csharp
private class ControlFlowScope extends ControlFlowElement {
private boolean exactScope;
ControlFlowScope() {
exists(ControlFlowReachabilityConfiguration c |
c.candidate(_, _, this, exactScope, _) or
c.candidateDef(_, _, this, exactScope, _)
)
}
predicate isExact() { exactScope = true }
predicate isNonExact() { exactScope = false }
}
private newtype TControlFlowElementOrBasicBlock =
TControlFlowElement(ControlFlowElement cfe) or
TBasicBlock(ControlFlow::BasicBlock bb)
class ControlFlowElementOrBasicBlock extends TControlFlowElementOrBasicBlock {
ControlFlowElement asControlFlowElement() { this = TControlFlowElement(result) }
ControlFlow::BasicBlock asBasicBlock() { this = TBasicBlock(result) }
string toString() {
result = this.asControlFlowElement().toString()
or
result = this.asBasicBlock().toString()
}
Location getLocation() {
result = this.asControlFlowElement().getLocation()
or
result = this.asBasicBlock().getLocation()
}
}
private predicate isBasicBlock(ControlFlowElementOrBasicBlock c) { c instanceof TBasicBlock }
private predicate isNonExactScope(ControlFlowElementOrBasicBlock c) {
c.asControlFlowElement().(ControlFlowScope).isNonExact()
}
private predicate step(ControlFlowElementOrBasicBlock pred, ControlFlowElementOrBasicBlock succ) {
pred.asBasicBlock().getANode().getAstNode() = succ.asControlFlowElement()
or
pred.asControlFlowElement() = succ.asControlFlowElement().getAChild()
}
private predicate basicBlockInNonExactScope(
ControlFlowElementOrBasicBlock bb, ControlFlowElementOrBasicBlock scope
) = doublyBoundedFastTC(step/2, isBasicBlock/1, isNonExactScope/1)(bb, scope)
pragma[noinline]
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
basicBlockInNonExactScope(TBasicBlock(result), TControlFlowElement(scope)) and
exactScope = false
or
scope.isExact() and
result.getANode().getAstNode() = scope and
exactScope = true
}
/**
* A helper class for determining control-flow reachability for pairs of
* elements.
*
* This is useful when defining for example expression-based data-flow steps in
* the presence of control-flow splitting, where a data-flow step should make
* sure to stay in the same split.
*
* For example, in
*
* ```csharp
* if (b)
* ....
* var x = "foo";
* if (b)
* ....
* ```
*
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
* `[b = true] "foo"` to `[b = false] SSA def(x)`
*/
abstract class ControlFlowReachabilityConfiguration extends string {
bindingset[this]
ControlFlowReachabilityConfiguration() { any() }
/**
* Holds if `e1` and `e2` are expressions for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
none()
}
/**
* Holds if `e` and `def` are elements for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
none()
}
pragma[nomagic]
private predicate reachesBasicBlockExprBase(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
ControlFlow::BasicBlock bb
) {
this.candidate(e1, e2, _, _, isSuccessor) and
cfn1 = e1.getAControlFlowNode() and
bb.getNode(i) = cfn1
}
pragma[nomagic]
private predicate reachesBasicBlockExprRec(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockExpr(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionBase(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
int i, ControlFlow::BasicBlock bb
) {
this.candidateDef(e, def, _, _, isSuccessor) and
cfn = e.getAControlFlowNode() and
bb.getNode(i) = cfn
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionRec(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinition(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
/**
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
*/
pragma[nomagic]
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
cfn2 = bb.getNode(j) and
cfn2 = e2.getAControlFlowNode()
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
cfn2 = bb.getANode() and
cfn2 = e2.getAControlFlowNode()
)
}
/**
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
*/
pragma[nomagic]
predicate hasDefPath(
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
cfnDef = bb.getNode(j) and
def.getExpr().getAControlFlowNode() = cfnDef
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
def.getExpr().getAControlFlowNode() = cfnDef and
cfnDef = bb.getANode()
)
}
}

View File

@@ -2,7 +2,6 @@ private import csharp
private import DataFlowPublic
private import DataFlowDispatch
private import DataFlowImplCommon
private import ControlFlowReachability
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
private import semmle.code.csharp.dataflow.internal.ExternalFlow
@@ -259,34 +258,16 @@ private module ThisFlow {
}
}
/**
* Holds if there is a control-flow path from `n1` to `n2`. `n2` is either an
* expression node or an SSA definition node.
*/
pragma[nomagic]
predicate hasNodePath(ControlFlowReachabilityConfiguration conf, ExprNode n1, Node n2) {
exists(ControlFlow::Node cfn1, ControlFlow::Node cfn2 | conf.hasExprPath(_, cfn1, _, cfn2) |
cfn1 = n1.getControlFlowNode() and
cfn2 = n2.(ExprNode).getControlFlowNode()
)
or
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
conf.hasDefPath(_, cfn, def, cfnDef) and
cfn = n1.getControlFlowNode() and
n2 = TAssignableDefinitionNode(def, cfnDef)
)
}
/** Provides logic related to captured variables. */
module VariableCapture {
private import codeql.dataflow.VariableCapture as Shared
private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks
private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) {
e1 = LocalFlow::getALastEvalNode(e2)
e1.getExpr() = LocalFlow::getALastEvalNode(e2.getExpr())
or
exists(Ssa::Definition def, AssignableDefinition adef |
LocalFlow::defAssigns(adef, _, e1) and
LocalFlow::defAssigns(adef, _, _, e1) and
def.getAnUltimateDefinition().(Ssa::ExplicitDefinition).getADefinition() = adef and
exists(def.getAReadAtNode(e2))
)
@@ -379,7 +360,7 @@ module VariableCapture {
this = def.getExpr().getAControlFlowNode()
}
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, result) }
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, _, result) }
CapturedVariable getVariable() { result = v }
}
@@ -528,127 +509,74 @@ module SsaFlow {
/** Provides predicates related to local data flow. */
module LocalFlow {
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
LocalExprStepConfiguration() { this = "LocalExprStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
(
e1 = e2.(ParenthesizedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(NullCoalescingExpr).getAnOperand() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(SuppressNullableWarningExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e2 =
any(ConditionalExpr ce |
e1 = ce.getThen() or
e1 = ce.getElse()
) and
scope = e2 and
isSuccessor = true
or
e1 = e2.(Cast).getExpr() and
scope = e2 and
isSuccessor = true
or
// An `=` expression, where the result of the expression is used
e2 =
any(AssignExpr ae |
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
e1 = ae.getRValue()
) and
scope = e2 and
isSuccessor = true
or
e1 = e2.(ObjectCreation).getInitializer() and
scope = e2 and
isSuccessor = false
or
e1 = e2.(ArrayCreation).getInitializer() and
scope = e2 and
isSuccessor = false
or
e1 = e2.(SwitchExpr).getACase().getBody() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(CheckedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(UncheckedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(CollectionExpression).getAnElement() and
e1 instanceof SpreadElementExpr and
scope = e2 and
isSuccessor = true
or
e1 = e2.(SpreadElementExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
exists(WithExpr we |
scope = we and
isSuccessor = true
|
e1 = we.getExpr() and
e2 = we.getInitializer()
or
e1 = we.getInitializer() and
e2 = we
)
or
scope = any(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1) and
isSuccessor = false
or
isSuccessor = true and
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
cfe.(IsExpr).getExpr() = e1 and scope = cfe
or
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1 and scope = sw)
)
predicate localExprStep(Expr e1, Expr e2) {
e1 = e2.(ParenthesizedExpr).getExpr()
or
e1 = e2.(NullCoalescingExpr).getAnOperand()
or
e1 = e2.(SuppressNullableWarningExpr).getExpr()
or
e2 =
any(ConditionalExpr ce |
e1 = ce.getThen() or
e1 = ce.getElse()
)
}
override predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
// Flow from source to definition
exactScope = false and
def.getSource() = e and
(
scope = def.getExpr() and
isSuccessor = true
or
scope = def.(AssignableDefinitions::PatternDefinition).getMatch().(IsExpr) and
isSuccessor = false
or
exists(Switch s |
s.getACase() = def.(AssignableDefinitions::PatternDefinition).getMatch() and
isSuccessor = true
|
scope = s.getExpr()
or
scope = s.getACase()
)
or
e1 = e2.(Cast).getExpr()
or
// An `=` expression, where the result of the expression is used
e2 =
any(AssignExpr ae |
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
e1 = ae.getRValue()
)
}
or
e1 = e2.(ObjectCreation).getInitializer()
or
e1 = e2.(ArrayCreation).getInitializer()
or
e1 = e2.(SwitchExpr).getACase().getBody()
or
e1 = e2.(CheckedExpr).getExpr()
or
e1 = e2.(UncheckedExpr).getExpr()
or
e1 = e2.(CollectionExpression).getAnElement() and
e1 instanceof SpreadElementExpr
or
e1 = e2.(SpreadElementExpr).getExpr()
or
exists(WithExpr we |
e1 = we.getExpr() and
e2 = we.getInitializer()
or
e1 = we.getInitializer() and
e2 = we
)
or
exists(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1)
or
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
cfe.(IsExpr).getExpr() = e1
or
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1)
)
}
predicate defAssigns(AssignableDefinition def, ControlFlow::Node cfnDef, ControlFlow::Node value) {
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
predicate defAssigns(
AssignableDefinition def, ControlFlow::Node cfnDef, Expr value, ControlFlow::Node valueCfn
) {
def.getSource() = value and
valueCfn = value.getControlFlowNode() and
cfnDef = def.getExpr().getAControlFlowNode()
}
private predicate defAssigns(ExprNode value, AssignableDefinitionNode defNode) {
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
defAssigns(def, cfnDef, value.getExpr(), _) and
cfn = value.getControlFlowNode() and
defNode = TAssignableDefinitionNode(def, cfnDef)
)
}
/**
@@ -659,7 +587,9 @@ module LocalFlow {
}
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
hasNodePath(any(LocalExprStepConfiguration x), nodeFrom, nodeTo)
localExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
or
defAssigns(nodeFrom, nodeTo)
or
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo) and
nodeFrom != nodeTo
@@ -685,11 +615,12 @@ module LocalFlow {
}
/**
* Gets a node that may execute last in `n`, and which, when it executes last,
* will be the value of `n`.
* Gets a node that may execute last in `e`, and which, when it executes last,
* will be the value of `e`.
*/
ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
exists(Expr e | any(LocalExprStepConfiguration x).hasExprPath(_, result, e, cfn) |
Expr getALastEvalNode(Expr e) {
localExprStep(result, e) and
(
e instanceof ConditionalExpr or
e instanceof Cast or
e instanceof NullCoalescingExpr or
@@ -713,9 +644,7 @@ module LocalFlow {
* we add a reverse flow step from `[post] b ? x : y` to `[post] x` and to
* `[post] y`, in order for the side-effect of `m` to reach both `x` and `y`.
*/
ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
result = getALastEvalNode(e)
}
Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
/**
* Holds if the value of `node2` is given by `node1`.
@@ -729,9 +658,10 @@ module LocalFlow {
e instanceof ThisAccess or e instanceof BaseAccess
)
or
hasNodePath(any(LocalExprStepConfiguration x), node1, node2) and
defAssigns(node1, node2)
or
localExprStep(node1.asExpr(), node2.asExpr()) and
(
node2 instanceof AssignableDefinitionNode or
node2.asExpr() instanceof Cast or
node2.asExpr() instanceof AssignExpr
)
@@ -775,12 +705,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
or
nodeTo = nodeFrom.(LocalFunctionCreationNode).getAnAccess(true)
or
nodeTo.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode() =
LocalFlow::getPostUpdateReverseStep(nodeFrom
.(PostUpdateNode)
.getPreUpdateNode()
.(ExprNode)
.getControlFlowNode())
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
LocalFlow::getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
) and
model = ""
or
@@ -834,11 +760,11 @@ private class Argument extends Expr {
}
/**
* Holds if `e` is an assignment of `src` to field or property `c` of `q`.
* Holds if there is an assignment of `src` to field or property `c` of `q`.
*
* `postUpdate` indicates whether the store targets a post-update node.
*/
private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, boolean postUpdate) {
private predicate fieldOrPropertyStore(ContentSet c, Expr src, Expr q, boolean postUpdate) {
exists(FieldOrProperty f |
c = f.getContentSet() and
(
@@ -861,25 +787,20 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
f = fa.getTarget() and
src = def.getSource() and
q = fa.getQualifier() and
e = def.getExpr() and
postUpdate = true
)
or
// `with` expression initializer, `x with { f = src }`
e =
any(WithExpr we |
exists(MemberInitializer mi |
q = we and
mi = we.getInitializer().getAMemberInitializer() and
f = mi.getInitializedMember() and
src = mi.getRValue() and
postUpdate = false
)
)
exists(WithExpr we, MemberInitializer mi |
q = we and
mi = we.getInitializer().getAMemberInitializer() and
f = mi.getInitializedMember() and
src = mi.getRValue() and
postUpdate = false
)
or
// Object initializer, `new C() { f = src }`
exists(MemberInitializer mi |
e = q and
mi = q.(ObjectInitializer).getAMemberInitializer() and
q.getParent() instanceof ObjectCreation and
f = mi.getInitializedMember() and
@@ -888,16 +809,13 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
)
or
// Tuple element, `(..., src, ...)` `f` is `ItemX` of tuple `q`
e =
any(TupleExpr te |
exists(int i |
e = q and
src = te.getArgument(i) and
te.isConstruction() and
f = q.getType().(TupleType).getElement(i) and
postUpdate = false
)
)
exists(TupleExpr te, int i |
te = q and
src = te.getArgument(i) and
te.isConstruction() and
f = q.getType().(TupleType).getElement(i) and
postUpdate = false
)
)
or
// A write to a dynamic property
@@ -907,7 +825,6 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
c.isDynamicProperty(dp) and
src = def.getSource() and
q = dma.getQualifier() and
e = def.getExpr() and
postUpdate = true
)
}
@@ -943,22 +860,20 @@ private predicate collectionStore(Expr src, CollectionExpression ce) {
}
/**
* Holds if `e` is an expression that adds `src` to array `a`.
* Holds if there is an expression that adds `src` to array `a`.
*
* `postUpdate` indicates whether the store targets a post-update node.
*/
private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
private predicate arrayStore(Expr src, Expr a, boolean postUpdate) {
// Direct assignment, `a[i] = src`
exists(AssignableDefinition def |
a = def.getTargetAccess().(ArrayWrite).getQualifier() and
src = def.getSource() and
e = def.getExpr() and
postUpdate = true
)
or
// Array initializer, `new [] { src }`
src = a.(ArrayInitializer).getAnElement() and
e = a and
postUpdate = false
or
// Member initializer, `new C { Array = { [i] = src } }`
@@ -966,7 +881,6 @@ private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
mi = a.(ObjectInitializer).getAMemberInitializer() and
mi.getLValue() instanceof ArrayAccess and
mi.getRValue() = src and
e = a and
postUpdate = false
)
}
@@ -1141,17 +1055,17 @@ private module Cached {
(
cfn.getExpr() instanceof Argument
or
cfn =
LocalFlow::getPostUpdateReverseStep(any(ControlFlow::Nodes::ExprNode e |
exists(any(SourcePostUpdateNode p).getPreUpdateNode().asExprAtNode(e))
))
cfn.getExpr() =
LocalFlow::getPostUpdateReverseStep(any(SourcePostUpdateNode p)
.getPreUpdateNode()
.asExpr())
) and
exprMayHavePostUpdateNode(cfn.getExpr())
or
exists(Expr e | e = cfn.getExpr() |
fieldOrPropertyStore(_, _, _, e, true)
fieldOrPropertyStore(_, _, e, true)
or
arrayStore(_, _, e, true)
arrayStore(_, e, true)
or
// needed for reverse stores; e.g. `x.f1.f2 = y` induces
// a store step of `f1` into `x`
@@ -1166,7 +1080,7 @@ private module Cached {
)
)
or
lambdaCallExpr(_, cfn)
lambdaCallExpr(_, _, cfn)
} or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
sn.getSummarizedCallable() instanceof CallableUsedInSource
@@ -1563,35 +1477,15 @@ abstract private class ArgumentNodeImpl extends Node {
}
private module ArgumentNodes {
private class ArgumentConfiguration extends ControlFlowReachabilityConfiguration {
ArgumentConfiguration() { this = "ArgumentConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e1.(Argument).isArgumentOf(e2, _) and
exactScope = false and
isSuccessor = true and
if e2 instanceof PropertyWrite
then
exists(AssignableDefinition def |
def.getTargetAccess() = e2 and
scope = def.getExpr()
)
else scope = e2
}
}
/** A data-flow node that represents an explicit call argument. */
class ExplicitArgumentNode extends ArgumentNodeImpl {
ExplicitArgumentNode() { this.asExpr() instanceof Argument }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
exists(ArgumentConfiguration x, Expr c, Argument arg |
exists(Expr c, Argument arg |
arg = this.asExpr() and
c = call.getExpr() and
arg.isArgumentOf(c, pos) and
x.hasExprPath(_, this.getControlFlowNode(), _, call.getControlFlowNode())
arg.isArgumentOf(c, pos)
)
}
}
@@ -1600,7 +1494,7 @@ private module ArgumentNodes {
class DelegateSelfArgumentNode extends ArgumentNodeImpl, ExprNode {
private DataFlowCall call_;
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getControlFlowNode()) }
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getExpr(), _) }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
call = call_ and
@@ -1857,27 +1751,6 @@ private module OutNodes {
}
}
class ObjectOrCollectionInitializerConfiguration extends ControlFlowReachabilityConfiguration {
ObjectOrCollectionInitializerConfiguration() {
this = "ObjectOrCollectionInitializerConfiguration"
}
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
scope = e1 and
isSuccessor = true and
exists(ObjectOrCollectionInitializer init | init = e1.(ObjectCreation).getInitializer() |
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
e2 = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
e2 = init.(ObjectInitializer).getAMemberInitializer().getLValue()
)
}
}
/**
* A data-flow node that reads a value returned by a callable using an
* `out` or `ref` parameter.
@@ -2236,30 +2109,6 @@ predicate jumpStep(Node pred, Node succ) {
succ = pred.(LocalFunctionCreationNode).getAnAccess(false)
}
private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration {
StoreStepConfiguration() { this = "StoreStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
fieldOrPropertyStore(scope, _, e1, e2, isSuccessor.booleanNot())
or
exactScope = false and
arrayStore(scope, e1, e2, isSuccessor.booleanNot())
or
exactScope = false and
isSuccessor = true and
collectionStore(e1, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
isParamsArg(e2, e1, _) and
scope = e2
}
}
pragma[nomagic]
private ContentSet getResultContent() {
result.isProperty(any(SystemThreadingTasksTaskTClass c_).getResultProperty())
@@ -2282,21 +2131,17 @@ private predicate recordParameter(RecordType t, Parameter p, string name) {
}
private predicate storeContentStep(Node node1, Content c, Node node2) {
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
hasNodePath(x, node1, node) and
exists(ExprNode node, boolean postUpdate |
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
arrayStore(node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
)
or
exists(StoreStepConfiguration x | hasNodePath(x, node1, node2) |
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
)
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
or
exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn |
x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and
node2 = TParamsArgumentNode(callCfn) and
isParamsArg(_, arg, _) and
exists(Call call |
node2 = TParamsArgumentNode(call.getControlFlowNode()) and
isParamsArg(call, node1.asExpr(), _) and
c instanceof ElementContent
)
or
@@ -2352,11 +2197,10 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
c.isSingleton(cont)
)
or
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
hasNodePath(x, node1, node) and
exists(ExprNode node, boolean postUpdate |
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
fieldOrPropertyStore(c, node1.asExpr(), node.getExpr(), postUpdate)
)
or
exists(Expr e |
@@ -2378,133 +2222,51 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
storeStepDelegateCall(node1, c, node2)
}
pragma[nomagic]
private predicate isAssignExprLValueDescendant(Expr e) {
e = any(AssignExpr ae).getLValue()
or
exists(Expr parent |
isAssignExprLValueDescendant(parent) and
e = parent.getAChildExpr()
)
}
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
ReadStepConfiguration() { this = "ReadStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
isSuccessor = true and
fieldOrPropertyRead(e1, _, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
dynamicPropertyRead(e1, _, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
arrayRead(e1, e2) and
scope = e2
or
exactScope = false and
e1 = e2.(AwaitExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
exactScope = false and
e2 = e1.(TupleExpr).getAnArgument() and
scope = e1 and
isSuccessor = false
}
override predicate candidateDef(
Expr e, AssignableDefinition defTo, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
exists(ForeachStmt fs |
e = fs.getIterableExpr() and
defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() =
fs.getVariableDeclExpr() and
isSuccessor = true
|
scope = fs and
exactScope = true
or
scope = fs.getIterableExpr() and
exactScope = false
or
scope = fs.getVariableDeclExpr() and
exactScope = false
)
or
scope =
any(AssignExpr ae |
ae = defTo.(AssignableDefinitions::TupleAssignmentDefinition).getAssignment() and
isAssignExprLValueDescendant(e.(TupleExpr)) and
exactScope = false and
isSuccessor = true
)
or
scope =
any(TupleExpr te |
te.getAnArgument() = defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() and
e = te and
exactScope = false and
isSuccessor = false
)
}
}
private predicate readContentStep(Node node1, Content c, Node node2) {
exists(ReadStepConfiguration x |
hasNodePath(x, node1, node2) and
arrayRead(node1.asExpr(), node2.asExpr()) and
arrayRead(node1.asExpr(), node2.asExpr()) and
c instanceof ElementContent
or
exists(
ForeachStmt fs, Ssa::ExplicitDefinition def,
AssignableDefinitions::LocalVariableDefinition defTo
|
node1.asExpr() = fs.getIterableExpr() and
defTo.getDeclaration() = fs.getVariableDeclExpr() and
def.getADefinition() = defTo and
node2.(SsaDefinitionNode).getDefinition() = def and
c instanceof ElementContent
)
or
node1 =
any(InstanceParameterAccessPreNode n |
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
) and
node2.asExpr() instanceof ParameterRead
or
// node1 = (..., node2, ...)
// node1.ItemX flows to node2
exists(TupleExpr te, int i, Expr item |
te = node1.asExpr() and
not te.isConstruction() and
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
// node1 = (..., item, ...)
te.getArgument(i) = item
|
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
node2.asExpr().(TupleExpr) = item
or
exists(ForeachStmt fs, Ssa::ExplicitDefinition def |
x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(),
def.getControlFlowNode()) and
node2.(SsaDefinitionNode).getDefinition() = def and
c instanceof ElementContent
// item = variable in node1 = (..., variable, ...)
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
node2.(AssignableDefinitionNode).getDefinition() = tad and
tad.getLeaf() = item
)
or
node1 =
any(InstanceParameterAccessPreNode n |
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
) and
node2.asExpr() instanceof ParameterRead
or
// node1 = (..., node2, ...)
// node1.ItemX flows to node2
exists(TupleExpr te, int i, Expr item |
te = node1.asExpr() and
not te.isConstruction() and
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
// node1 = (..., item, ...)
te.getArgument(i) = item
|
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
node2.asExpr().(TupleExpr) = item and
hasNodePath(x, node1, node2)
or
// item = variable in node1 = (..., variable, ...)
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
node2.(AssignableDefinitionNode).getDefinition() = tad and
tad.getLeaf() = item and
hasNodePath(x, node1, node2)
)
or
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
isPatternExprDescendant(te) and
exists(AssignableDefinitions::LocalVariableDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item and
hasNodePath(x, node1, node2)
)
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
isPatternExprDescendant(te) and
exists(AssignableDefinitions::LocalVariableDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item
)
)
or
@@ -2535,14 +2297,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
c.isSingleton(cont)
)
or
exists(ReadStepConfiguration x | hasNodePath(x, node1, node2) |
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
or
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
or
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
c = getResultContent()
)
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
or
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
or
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
c = getResultContent()
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
@@ -2576,9 +2336,9 @@ predicate clearsContent(Node n, ContentSet c) {
c.isSingleton(cont)
)
or
fieldOrPropertyStore(_, c, _, n.asExpr(), true)
fieldOrPropertyStore(c, _, n.asExpr(), true)
or
fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
fieldOrPropertyStore(c, _, n.(ObjectInitializerNode).getInitializer(), false)
or
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
or
@@ -2817,8 +2577,13 @@ module PostUpdateNodes {
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
pos.isQualifier() and
any(ObjectOrCollectionInitializerConfiguration x)
.hasExprPath(_, cfn, _, call.getControlFlowNode())
exists(ObjectOrCollectionInitializer init | init = oc.getInitializer() |
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
call.getExpr() = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
call.getExpr() = init.(ObjectInitializer).getAMemberInitializer().getLValue()
)
}
override DataFlowCallable getEnclosingCallableImpl() {
@@ -2980,45 +2745,26 @@ private predicate isLocalFunctionCallReceiver(
f = receiver.getTarget().getUnboundDeclaration()
}
private class LambdaConfiguration extends ControlFlowReachabilityConfiguration {
LambdaConfiguration() { this = "LambdaConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e1 = e2.(DelegateLikeCall).getExpr() and
exactScope = false and
scope = e2 and
isSuccessor = true
or
e1 = e2.(DelegateCreation).getArgument() and
exactScope = false and
scope = e2 and
isSuccessor = true
or
isLocalFunctionCallReceiver(e2, e1, _) and
exactScope = false and
scope = e2 and
isSuccessor = true
}
}
private predicate lambdaCallExpr(DataFlowCall call, ControlFlow::Node receiver) {
exists(LambdaConfiguration x, DelegateLikeCall dc |
x.hasExprPath(dc.getExpr(), receiver, dc, call.getControlFlowNode())
private predicate lambdaCallExpr(DataFlowCall call, Expr receiver, ControlFlow::Node receiverCfn) {
exists(DelegateLikeCall dc |
call.(ExplicitDelegateLikeDataFlowCall).getCall() = dc and
receiver = dc.getExpr() and
receiverCfn = receiver.getControlFlowNode()
)
or
// In local function calls, `F()`, we use the local function access `F`
// to represent the receiver. Only needed for flow through captured variables.
exists(LambdaConfiguration x, LocalFunctionCall fc |
x.hasExprPath(fc.getAChild(), receiver, fc, call.getControlFlowNode())
exists(LocalFunctionCall fc |
receiver = fc.getAChild() and
receiverCfn = receiver.getControlFlowNode() and
fc.getControlFlowNode() = call.getControlFlowNode()
)
}
/** Holds if `call` is a lambda call where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
lambdaCallExpr(call, receiver.(ExprNode).getControlFlowNode()) and
lambdaCallExpr(call, receiver.asExpr(), _) and
// local function calls can be resolved directly without a flow analysis
not call.getControlFlowNode().getAstNode() instanceof LocalFunctionCall
or
@@ -3028,9 +2774,9 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
}
private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
exists(LambdaConfiguration x, DelegateCreation dc |
x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
nodeTo.(ExprNode).getControlFlowNode())
exists(DelegateCreation dc |
dc.getArgument() = nodeFrom.asExpr() and
dc = nodeTo.asExpr()
)
}

View File

@@ -4,7 +4,6 @@ private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.Caching
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.ControlFlowReachability
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.commons.ComparisonTest
// import `TaintedMember` definitions from other files to avoid potential reevaluation
@@ -45,82 +44,58 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c)
)
}
private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityConfiguration {
LocalTaintExprStepConfiguration() { this = "LocalTaintExprStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
isSuccessor = true and
(
e1 = e2.(ElementAccess).getQualifier() and
scope = e2
or
e1 = e2.(AddExpr).getAnOperand() and
scope = e2
or
// A comparison expression where taint can flow from one of the
// operands if the other operand is a constant value.
exists(ComparisonTest ct, Expr other |
ct.getExpr() = e2 and
e1 = ct.getAnArgument() and
other = ct.getAnArgument() and
other.stripCasts().hasValue() and
e1 != other and
scope = e2
)
or
e1 = e2.(UnaryLogicalOperation).getAnOperand() and
scope = e2
or
e1 = e2.(BinaryLogicalOperation).getAnOperand() and
scope = e2
or
e1 = e2.(InterpolatedStringExpr).getAChild() and
scope = e2
or
e1 = e2.(InterpolatedStringInsertExpr).getInsert() and
scope = e2
or
e2 =
any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
e1 = oc.getAnArgument() and
scope = e2
)
or
e1 = e2.(AwaitExpr).getExpr() and
scope = e2
or
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
e2 =
any(CastExpr ce |
e1 = ce.getExpr() and
scope = ce and
ce.getTargetType()
.(Attributable)
.getAnAttribute()
.getType()
.hasFullyQualifiedName("System.Runtime.CompilerServices",
"InterpolatedStringHandlerAttribute")
)
)
}
}
private ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
exists(OperatorCall oc | any(LocalTaintExprStepConfiguration x).hasExprPath(_, result, oc, cfn) |
oc.getTarget() instanceof ImplicitConversionOperator
private predicate localTaintExprStep(Expr e1, Expr e2) {
e1 = e2.(ElementAccess).getQualifier()
or
e1 = e2.(AddExpr).getAnOperand()
or
// A comparison expression where taint can flow from one of the
// operands if the other operand is a constant value.
exists(ComparisonTest ct, Expr other |
ct.getExpr() = e2 and
e1 = ct.getAnArgument() and
other = ct.getAnArgument() and
other.stripCasts().hasValue() and
e1 != other
)
or
e1 = e2.(UnaryLogicalOperation).getAnOperand()
or
e1 = e2.(BinaryLogicalOperation).getAnOperand()
or
e1 = e2.(InterpolatedStringExpr).getAChild()
or
e1 = e2.(InterpolatedStringInsertExpr).getInsert()
or
e2 =
any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
e1 = oc.getAnArgument()
)
or
e1 = e2.(AwaitExpr).getExpr()
or
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
e2 =
any(CastExpr ce |
e1 = ce.getExpr() and
ce.getTargetType()
.(Attributable)
.getAnAttribute()
.getType()
.hasFullyQualifiedName("System.Runtime.CompilerServices",
"InterpolatedStringHandlerAttribute")
)
}
private ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
result = getALastEvalNode(e)
private Expr getALastEvalNode(OperatorCall oc) {
localTaintExprStep(result, oc) and oc.getTarget() instanceof ImplicitConversionOperator
}
private Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
hasNodePath(any(LocalTaintExprStepConfiguration x), nodeFrom, nodeTo)
localTaintExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
}
cached
@@ -191,12 +166,8 @@ private module Cached {
// Allow reverse update flow for implicit conversion operator calls.
// This is needed to support flow out of method call arguments, where an implicit conversion is applied
// to a call argument.
nodeTo.(PostUpdateNode).getPreUpdateNode().(DataFlow::ExprNode).getControlFlowNode() =
getPostUpdateReverseStep(nodeFrom
.(PostUpdateNode)
.getPreUpdateNode()
.(DataFlow::ExprNode)
.getControlFlowNode())
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
) and
model = ""
or

View File

@@ -1,246 +0,0 @@
import csharp
private class ControlFlowScope extends ControlFlowElement {
private boolean exactScope;
ControlFlowScope() {
exists(ControlFlowReachabilityConfiguration c |
c.candidate(_, _, this, exactScope, _) or
c.candidateDef(_, _, this, exactScope, _)
)
}
predicate isExact() { exactScope = true }
predicate isNonExact() { exactScope = false }
}
private newtype TControlFlowElementOrBasicBlock =
TControlFlowElement(ControlFlowElement cfe) or
TBasicBlock(ControlFlow::BasicBlock bb)
class ControlFlowElementOrBasicBlock extends TControlFlowElementOrBasicBlock {
ControlFlowElement asControlFlowElement() { this = TControlFlowElement(result) }
ControlFlow::BasicBlock asBasicBlock() { this = TBasicBlock(result) }
string toString() {
result = this.asControlFlowElement().toString()
or
result = this.asBasicBlock().toString()
}
Location getLocation() {
result = this.asControlFlowElement().getLocation()
or
result = this.asBasicBlock().getLocation()
}
}
private predicate isBasicBlock(ControlFlowElementOrBasicBlock c) { c instanceof TBasicBlock }
private predicate isNonExactScope(ControlFlowElementOrBasicBlock c) {
c.asControlFlowElement().(ControlFlowScope).isNonExact()
}
private predicate step(ControlFlowElementOrBasicBlock pred, ControlFlowElementOrBasicBlock succ) {
pred.asBasicBlock().getANode().getAstNode() = succ.asControlFlowElement()
or
pred.asControlFlowElement() = succ.asControlFlowElement().getAChild()
}
private predicate basicBlockInNonExactScope(
ControlFlowElementOrBasicBlock bb, ControlFlowElementOrBasicBlock scope
) = doublyBoundedFastTC(step/2, isBasicBlock/1, isNonExactScope/1)(bb, scope)
pragma[noinline]
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
basicBlockInNonExactScope(TBasicBlock(result), TControlFlowElement(scope)) and
exactScope = false
or
scope.isExact() and
result.getANode().getAstNode() = scope and
exactScope = true
}
/**
* A helper class for determining control-flow reachability for pairs of
* elements.
*
* This is useful when defining for example expression-based data-flow steps in
* the presence of control-flow splitting, where a data-flow step should make
* sure to stay in the same split.
*
* For example, in
*
* ```csharp
* if (b)
* ....
* var x = "foo";
* if (b)
* ....
* ```
*
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
* `[b = true] "foo"` to `[b = false] SSA def(x)`
*/
abstract class ControlFlowReachabilityConfiguration extends string {
bindingset[this]
ControlFlowReachabilityConfiguration() { any() }
/**
* Holds if `e1` and `e2` are expressions for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
none()
}
/**
* Holds if `e` and `def` are elements for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
none()
}
pragma[nomagic]
private predicate reachesBasicBlockExprBase(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
ControlFlow::BasicBlock bb
) {
this.candidate(e1, e2, _, _, isSuccessor) and
cfn1 = e1.getAControlFlowNode() and
bb.getNode(i) = cfn1
}
pragma[nomagic]
private predicate reachesBasicBlockExprRec(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockExpr(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionBase(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
int i, ControlFlow::BasicBlock bb
) {
this.candidateDef(e, def, _, _, isSuccessor) and
cfn = e.getAControlFlowNode() and
bb.getNode(i) = cfn
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionRec(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinition(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
/**
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
*/
pragma[nomagic]
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
cfn2 = bb.getNode(j) and
cfn2 = e2.getAControlFlowNode()
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
cfn2 = bb.getANode() and
cfn2 = e2.getAControlFlowNode()
)
}
/**
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
*/
pragma[nomagic]
predicate hasDefPath(
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
cfnDef = bb.getNode(j) and
def.getExpr().getAControlFlowNode() = cfnDef
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
def.getExpr().getAControlFlowNode() = cfnDef and
cfnDef = bb.getANode()
)
}
}

View File

@@ -8,26 +8,14 @@ private module Impl {
private import ConstantUtils
private import SsaReadPositionCommon
private import semmle.code.csharp.controlflow.Guards as G
private import ControlFlowReachability
private class ExprNode = ControlFlow::Nodes::ExprNode;
private class ExprChildReachability extends ControlFlowReachabilityConfiguration {
ExprChildReachability() { this = "ExprChildReachability" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e2 = e1.getAChild() and
scope = e1 and
exactScope = false and
isSuccessor in [false, true]
}
}
/** Holds if `parent` having child `child` implies `parentNode` having child `childNode`. */
predicate hasChild(Expr parent, Expr child, ExprNode parentNode, ExprNode childNode) {
any(ExprChildReachability x).hasExprPath(parent, parentNode, child, childNode)
parent.getAChild() = child and
parentNode = parent.getControlFlowNode() and
childNode = child.getControlFlowNode()
}
/** Holds if SSA definition `def` equals `e + delta`. */

View File

@@ -4,7 +4,7 @@
* allows for a cross-site scripting vulnerability.
* @kind path-problem
* @problem.severity error
* @security-severity 6.1
* @security-severity 7.8
* @precision high
* @id cs/web/xss
* @tags security

View File

@@ -4,7 +4,7 @@
* insertion of forged log entries by a malicious user.
* @kind path-problem
* @problem.severity error
* @security-severity 7.8
* @security-severity 6.1
* @precision high
* @id cs/log-forging
* @tags security

View File

@@ -0,0 +1,5 @@
---
category: queryMetadata
---
* The `@security-severity` metadata of `cs/log-forging` has been reduced from 7.8 (high) to 6.1 (medium).
* The `@security-severity` metadata of `cs/web/xss` has been increased from 6.1 (medium) to 7.8 (high).

View File

@@ -26,6 +26,7 @@
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:16:31:20 | ... > ... |
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:24:31:24 | access to parameter i |
| CSharp7.cs:31:24:31:24 | access to parameter i | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
| CSharp7.cs:31:28:31:59 | throw ... | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
| CSharp7.cs:35:7:35:18 | this | CSharp7.cs:35:7:35:18 | this access |
| CSharp7.cs:39:9:39:9 | access to parameter x | CSharp7.cs:39:9:39:21 | SSA def(x) |
| CSharp7.cs:39:13:39:21 | "tainted" | CSharp7.cs:39:9:39:9 | access to parameter x |
@@ -253,6 +254,7 @@
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:235:13:235:42 | [input] SSA phi read(o) |
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:237:18:237:18 | access to local variable o |
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
| CSharp7.cs:233:18:233:23 | Int32 i1 | CSharp7.cs:233:18:233:23 | SSA def(i1) |
@@ -338,6 +340,8 @@
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:40:297:44 | Int32 y |
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:49:297:49 | access to local variable x |
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
| CSharp7.cs:297:40:297:44 | Int32 y | CSharp7.cs:297:40:297:44 | SSA def(y) |
| CSharp7.cs:297:40:297:44 | SSA def(y) | CSharp7.cs:299:31:299:31 | access to local variable y |

View File

@@ -24,6 +24,7 @@
| ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:172:21:172:33 | MethodWithOut |
| ExactCallable.cs:15:25:15:35 | Run`2 | ExactCallable.cs:177:21:177:34 | MethodWithOut2 |
| ExactCallable.cs:182:21:182:22 | M1 | ExactCallable.cs:187:21:187:22 | M2 |
| TypeFlow.cs:3:7:3:14 | <object initializer> | TypeFlow.cs:22:20:22:22 | set_Prop |
| TypeFlow.cs:5:5:5:12 | TypeFlow | TypeFlow.cs:24:10:24:12 | Run |
| TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:12:29:12:34 | Method |
| TypeFlow.cs:24:10:24:12 | Run | TypeFlow.cs:17:30:17:35 | Method |

View File

@@ -56,11 +56,11 @@ gvn
| StructuralComparison.cs:3:14:3:18 | this access | (kind:Expr(12),false,Class) |
| StructuralComparison.cs:3:14:3:18 | {...} | (kind:Stmt(1)) |
| StructuralComparison.cs:5:26:5:26 | access to field x | (kind:Expr(16),true,x) |
| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12)) |
| StructuralComparison.cs:5:26:5:26 | this access | (kind:Expr(12),false,Class) |
| StructuralComparison.cs:5:26:5:30 | ... = ... | ((kind:Expr(16),true,x) :: (0 :: (kind:Expr(63)))) |
| StructuralComparison.cs:5:30:5:30 | 0 | 0 |
| StructuralComparison.cs:6:26:6:26 | access to field y | (kind:Expr(16),true,y) |
| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12)) |
| StructuralComparison.cs:6:26:6:26 | this access | (kind:Expr(12),false,Class) |
| StructuralComparison.cs:6:26:6:30 | ... = ... | ((kind:Expr(16),true,y) :: (1 :: (kind:Expr(63)))) |
| StructuralComparison.cs:6:30:6:30 | 1 | 1 |
| StructuralComparison.cs:8:24:8:24 | 0 | 0 |

View File

@@ -406,7 +406,7 @@ Adds a new taint source. Most taint-tracking queries will use the new source.
- **type**: Name of a type from which to evaluate **path**.
- **path**: Access path leading to the source.
- **kind**: Kind of source to add. Currently only **remote** is used.
- **kind**: Kind of source to add. See the section on source kinds for a list of supported kinds.
Example:
@@ -553,7 +553,16 @@ Kinds
Source kinds
~~~~~~~~~~~~
See documentation below for :ref:`Threat models <threat-models-javascript>`.
- **remote**: A general source of remote flow.
- **browser**: A source in the browser environment that does not fit a more specific browser kind.
- **browser-url-query**: A source derived from the query parameters of the browser URL, such as ``location.search``.
- **browser-url-fragment**: A source derived from the fragment part of the browser URL, such as ``location.hash``.
- **browser-url-path**: A source derived from the pathname of the browser URL, such as ``location.pathname``.
- **browser-url**: A source derived from the browser URL, where the untrusted part is prefixed by trusted data such as the scheme and hostname.
- **browser-window-name**: A source derived from the window name, such as ``window.name``.
- **browser-message-event**: A source derived from cross-window message passing, such as ``event`` in ``window.onmessage = event => {...}``.
See also :ref:`Threat models <threat-models-javascript>`.
Sink kinds
~~~~~~~~~~

View File

@@ -9,8 +9,8 @@ toolchain go1.26.0
// when adding or removing dependencies, run
// bazel mod tidy
require (
golang.org/x/mod v0.33.0
golang.org/x/tools v0.42.0
golang.org/x/mod v0.34.0
golang.org/x/tools v0.43.0
)
require github.com/stretchr/testify v1.11.1
@@ -18,6 +18,6 @@ require github.com/stretchr/testify v1.11.1
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sync v0.20.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -6,12 +6,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -8,5 +8,7 @@
* `FileSystemAccess`, or the `Source` and `Sink` classes associated with the security queries
* to model frameworks that are not covered by the standard library.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with Go programs.
*/
overlay[local?]
module;
import Customizations
import semmle.go.Architectures

View File

@@ -2,6 +2,8 @@
* Provides classes and predicates related to contextual queries
* in the code viewer.
*/
overlay[local?]
module;
import go
private import codeql.util.FileSystem

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with AST nodes.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with architectures. */
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with code comments.
*/
overlay[local]
module;
import go

View File

@@ -3,6 +3,8 @@
* access or system command execution, for which individual framework libraries
* provide concrete subclasses.
*/
overlay[local?]
module;
import go
import semmle.go.dataflow.FunctionInputsAndOutputs

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with declarations.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with errors and warnings recorded during extraction. */
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with Go frontend errors recorded during extraction. */
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with expressions.
*/
overlay[local]
module;
import go
@@ -829,7 +831,7 @@ class ConversionExpr extends CallOrConversionExpr {
/**
* A function call expression.
*
* On snapshots with incomplete type information, type conversions may be misclassified
* On databases with incomplete type information, type conversions may be misclassified
* as function call expressions.
*
* Examples:
@@ -2035,6 +2037,9 @@ class ConstantName extends ValueName {
override string getAPrimaryQlClass() { result = "ConstantName" }
}
/** Holds if `e` is an expression that refers to the `nil` constant. */
predicate exprRefersToNil(Expr e) { e.(ConstantName).getTarget() = Builtin::nil() }
/**
* A name referring to a variable.
*
@@ -2093,7 +2098,7 @@ class LabelName extends Name {
* Holds if `e` is a type expression, as determined by a bottom-up syntactic
* analysis starting with `TypeName`s.
*
* On a snapshot with full type information, this predicate covers all type
* On a database with full type information, this predicate covers all type
* expressions. However, if type information is missing then not all type names
* may be identified as such, so not all type expressions can be determined by
* a bottom-up analysis. In such cases, `isTypeExprTopDown` below is useful.
@@ -2131,11 +2136,12 @@ private predicate isTypeExprBottomUp(Expr e) {
* Holds if `e` must be a type expression because it either occurs in a syntactic
* position where a type is expected, or it is part of a larger type expression.
*
* This predicate is only needed on snapshots for which type information is
* incomplete. It is an underapproximation; in cases where it is syntactically ambiguous
* whether an expression refers to a type or a value, we conservatively assume that
* it may be the latter and so this predicate does not consider the expression to be
* a type expression.
* This predicate is only needed on databases for which type information is
* incomplete - for example, when some dependencies could not be reached during
* extraction. It is an underapproximation; in cases where it is syntactically
* ambiguous whether an expression refers to a type or a value, we conservatively
* assume that it may be the latter and so this predicate does not consider the
* expression to be a type expression.
*/
pragma[nomagic]
private predicate isTypeExprTopDown(Expr e) {
@@ -2145,20 +2151,12 @@ private predicate isTypeExprTopDown(Expr e) {
or
e = any(ArrayTypeExpr ae).getElement()
or
e = any(FieldDecl f).getTypeExpr()
or
e = any(ParameterDecl pd).getTypeExpr()
e = any(FieldBase fb).getTypeExpr()
or
e = any(TypeParamDecl tpd).getTypeConstraintExpr()
or
e = any(TypeParamDecl tpd).getNameExpr(_)
or
e = any(ReceiverDecl rd).getTypeExpr()
or
e = any(ResultVariableDecl rvd).getTypeExpr()
or
e = any(MethodSpec md).getTypeExpr()
or
e = any(MapTypeExpr mt).getKeyTypeExpr()
or
e = any(MapTypeExpr mt).getValueTypeExpr()
@@ -2175,7 +2173,7 @@ private predicate isTypeExprTopDown(Expr e) {
or
e = any(TypeSwitchStmt s).getACase().getExpr(_) and
// special case: `nil` is allowed in a type case but isn't a type
not e = Builtin::nil().getAReference()
not exprRefersToNil(e)
or
e = any(SelectorExpr sel | isTypeExprTopDown(sel)).getBase()
or

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with files and folders. */
overlay[local]
module;
import go
private import codeql.util.FileSystem

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with go.mod files.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with HTML documents. */
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with locations and program elements that have locations. */
overlay[local]
module;
import go
private import semmle.go.Overlay

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with packages.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides queries to pretty-print a Go AST as a graph.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with scopes and declared objects.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with statements.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides predicates and classes for working with string operations.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with Go types.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** This module provides general utility classes and predicates. */
overlay[local]
module;
/**
* A Boolean value.

View File

@@ -1,4 +1,6 @@
/** Provides the `VariableWithFields` class, for working with variables with a chain of field or element accesses chained to it. */
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides a class for generated files. */
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with HTTP-related concepts such as requests and responses.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with basic blocks.
*/
overlay[local]
module;
import go
private import ControlFlowGraphImpl

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with a CFG-based program representation.
*/
overlay[local]
module;
import go
private import ControlFlowGraphImpl
@@ -62,6 +64,7 @@ module ControlFlow {
BasicBlock getBasicBlock() { result.getANode() = this }
/** Holds if this node dominates `dominee` in the control-flow graph. */
overlay[caller?]
pragma[inline]
predicate dominatesNode(ControlFlow::Node dominee) {
exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j |

View File

@@ -3,6 +3,8 @@
*
* Provides predicates for building intra-procedural CFGs.
*/
overlay[local]
module;
import go

View File

@@ -9,6 +9,8 @@
* Each instruction is also a control-flow node, but there are control-flow nodes that are not
* instructions (synthetic entry and exit nodes, as well as no-op skip nodes).
*/
overlay[local]
module;
import go
private import semmle.go.controlflow.ControlFlowGraphImpl
@@ -294,7 +296,7 @@ module IR {
/**
* An IR instruction that reads the value of a field.
*
* On snapshots with incomplete type information, method expressions may sometimes be
* On databases with incomplete type information, method expressions may sometimes be
* misclassified as field reads.
*/
class FieldReadInstruction extends ComponentReadInstruction {

View File

@@ -14,6 +14,8 @@
* (intraprocedural) data flow, invoke `DataFlow::localFlow` or
* `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`.
*/
overlay[local?]
module;
import go

View File

@@ -84,6 +84,8 @@
* "taint" indicates a default additional taint step and "value" indicates a
* globally applicable value-preserving step.
*/
overlay[local?]
module;
private import go
private import internal.ExternalFlowExtensions::Extensions as Extensions

View File

@@ -1,6 +1,8 @@
/**
* Provides classes and predicates for defining flow summaries.
*/
overlay[local?]
module;
import go
private import internal.FlowSummaryImpl as Impl

View File

@@ -2,6 +2,8 @@
* Provides QL classes for indicating data flow through a function parameter, return value,
* or receiver.
*/
overlay[local]
module;
import go
private import semmle.go.dataflow.internal.DataFlowPrivate

View File

@@ -29,6 +29,8 @@
* common reason for this is that the analysis cannot prove that there
* are no side-effects that might cause the computed value to change.
*/
overlay[local]
module;
/*
* Note to developers: the correctness of this module depends on the

View File

@@ -1,6 +1,8 @@
/**
* Provides a class for representing and reasoning about properties of data-flow nodes.
*/
overlay[local]
module;
import go
@@ -22,7 +24,7 @@ class Property extends TProperty {
isTrue = eq.getPolarity().booleanXor(e.getBoolValue().booleanXor(outcome))
or
this = IsNil(isTrue) and
e = Builtin::nil().getAReference() and
exprRefersToNil(e) and
isTrue = eq.getPolarity().booleanXor(outcome).booleanNot()
)
or

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with static single assignment form (SSA).
*/
overlay[local]
module;
import go
private import SsaImpl

View File

@@ -3,6 +3,8 @@
*
* Provides predicates for constructing an SSA representation for functions.
*/
overlay[local]
module;
import go

View File

@@ -2,6 +2,8 @@
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*/
overlay[local?]
module;
import semmle.go.dataflow.DataFlow

View File

@@ -1,6 +1,8 @@
/**
* Provides an implementation of a commonly used barrier guard for sanitizing untrusted URLs.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides an implementation of a commonly used barrier guard for sanitizing untrusted URLs.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides an implementation of a commonly used barrier guard for sanitizing untrusted URLs.
*/
overlay[local?]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Contains predicates for dealing with container flow. */
overlay[local?]
module;
import go
private import DataFlowNodes

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import go
private import DataFlowPrivate
@@ -119,6 +122,7 @@ class ArgumentPosition extends int {
}
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
overlay[caller?]
pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
@@ -130,6 +134,7 @@ private predicate isInterfaceMethod(Method c) {
* Holds if `call` is passing `arg` to param `p` in any circumstance except passing
* a receiver parameter to a concrete method.
*/
overlay[caller?]
pragma[inline]
predicate golangSpecificParamArgFilter(
DataFlowCall call, DataFlow::ParameterNode p, DataFlow::ArgumentNode arg

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import DataFlowImplSpecific
private import codeql.dataflow.internal.DataFlowImpl
private import semmle.go.Locations

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import DataFlowImplSpecific
private import codeql.dataflow.internal.DataFlowImplCommon
private import semmle.go.Locations

View File

@@ -2,6 +2,8 @@
* Provides consistency queries for checking invariants in the language-specific
* data-flow classes and predicates.
*/
overlay[local?]
module;
private import go
private import DataFlowImplSpecific as Impl

View File

@@ -1,6 +1,8 @@
/**
* Provides Go-specific definitions for use in the data flow library.
*/
overlay[local?]
module;
private import codeql.dataflow.DataFlow
private import semmle.go.Locations

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
private import go
private import semmle.go.dataflow.FunctionInputsAndOutputs
private import semmle.go.dataflow.FlowSummary

View File

@@ -1,3 +1,6 @@
overlay[local?]
module;
private import go
private import DataFlowUtil
private import DataFlowImplCommon
@@ -478,5 +481,6 @@ predicate allowParameterReturnInSelf(ParameterNode p) {
class ContentApprox = Unit;
/** Gets an approximated value for content `c`. */
overlay[caller?]
pragma[inline]
ContentApprox getContentApprox(Content c) { any() }

View File

@@ -1,6 +1,8 @@
/**
* Provides Go-specific definitions for use in the data flow library.
*/
overlay[local?]
module;
private import go
private import semmle.go.dataflow.FunctionInputsAndOutputs
@@ -147,6 +149,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
* Holds if data flows from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
overlay[caller?]
pragma[inline]
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
@@ -560,7 +563,7 @@ private predicate onlyPossibleReturnOfBool(FuncDecl fd, FunctionOutput res, Node
*/
predicate possiblyReturnsNonNil(FuncDecl fd, FunctionOutput res, Node ret) {
ret = res.getEntryNode(fd) and
not ret.asExpr() = Builtin::nil().getAReference()
not exprRefersToNil(ret.asExpr())
}
/**
@@ -570,7 +573,7 @@ predicate possiblyReturnsNonNil(FuncDecl fd, FunctionOutput res, Node ret) {
private predicate onlyPossibleReturnOfNonNil(FuncDecl fd, FunctionOutput res, Node ret) {
possiblyReturnsNonNil(fd, res, ret) and
forall(Node otherRet | otherRet = res.getEntryNode(fd) and otherRet != ret |
otherRet.asExpr() = Builtin::nil().getAReference()
exprRefersToNil(otherRet.asExpr())
)
}
@@ -609,7 +612,7 @@ private predicate isCertainlyNotNil(DataFlow::Node node) {
*/
private predicate onlyPossibleReturnOfNil(FuncDecl fd, FunctionOutput res, DataFlow::Node ret) {
ret = res.getEntryNode(fd) and
ret.asExpr() = Builtin::nil().getAReference() and
exprRefersToNil(ret.asExpr()) and
forall(DataFlow::Node otherRet | otherRet = res.getEntryNode(fd) and otherRet != ret |
isCertainlyNotNil(otherRet)
)

View File

@@ -1,6 +1,8 @@
/**
* This module provides extensible predicates for defining MaD models.
*/
overlay[local?]
module;
private import codeql.mad.static.ModelsAsData as SharedMaD

View File

@@ -1,6 +1,8 @@
/**
* Provides classes and predicates for defining flow summaries.
*/
overlay[local?]
module;
private import go
private import codeql.dataflow.internal.FlowSummaryImpl

View File

@@ -1,6 +1,8 @@
/**
* Provides Go-specific definitions for use in the taint tracking library.
*/
overlay[local?]
module;
private import codeql.dataflow.TaintTracking
private import DataFlowImplSpecific

View File

@@ -1,6 +1,8 @@
/**
* Provides Go-specific definitions for use in the taint-tracking library.
*/
overlay[local?]
module;
private import go
private import FlowSummaryImpl as FlowSummaryImpl
@@ -11,6 +13,7 @@ private import DataFlowPrivate as DataFlowPrivate
* Holds if taint can flow from `src` to `sink` in zero or more
* local (intra-procedural) steps.
*/
overlay[caller?]
pragma[inline]
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) }
@@ -18,6 +21,7 @@ predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(
* Holds if taint can flow from `src` to `sink` in zero or more
* local (intra-procedural) steps.
*/
overlay[caller?]
pragma[inline]
predicate localExprTaint(Expr src, Expr sink) {
localTaint(DataFlow::exprNode(src), DataFlow::exprNode(sink))
@@ -418,7 +422,7 @@ predicate functionEnsuresInputIsConstant(
forex(DataFlow::Node ret, IR::ReturnInstruction ri |
ret = outp.getEntryNode(fd) and
ri.getReturnStmt().getAnExpr() = ret.asExpr() and
ret.asExpr() = Builtin::nil().getAReference()
exprRefersToNil(ret.asExpr())
|
DataFlow::localFlow(inp.getExitNode(fd), _) and
mustPassConstantCaseTestToReach(ri, inp.getExitNode(fd))

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for modeling go.mod dependencies.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for dealing with semantic versions, for dependency versions.
*/
overlay[local?]
module;
import semmle.go.dependencies.Dependencies

View File

@@ -2,6 +2,8 @@
* Provides classes for working with sinks and taint propagators
* from the `github.com/spf13/afero` package.
*/
overlay[local?]
module;
import go

View File

@@ -2,6 +2,8 @@
* Provides classes for working with remote flow sources, sinks and taint propagators
* from the `github.com/aws/aws-lambda-go/lambda` package.
*/
overlay[local?]
module;
import go

View File

@@ -2,6 +2,8 @@
* Provides classes for working with remote flow sources, sinks and taint propagators
* from the `github.com/beego/beego` package.
*/
overlay[local?]
module;
import go
import semmle.go.security.Xss

View File

@@ -2,6 +2,8 @@
* Provides classes for working with remote flow sources, sinks and taint propagators
* from the `github.com/astaxie/beego/orm` subpackage.
*/
overlay[local?]
module;
import go
private import semmle.go.security.StoredXssCustomizations

View File

@@ -1,6 +1,8 @@
/**
* Provides classes modeling security-relevant aspects of the `Bun` package.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides models of commonly used functions in the official Couchbase Go SDK library.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for modeling cryptographic libraries.
*/
overlay[local?]
module;
import go
import semmle.go.Concepts::Cryptography

View File

@@ -2,6 +2,8 @@
* Provides classes for working with remote flow sources, taint propagators, and HTTP sinks
* from the `github.com/labstack/echo` package.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with concepts relating to the [github.com/elazarl/goproxy](https://pkg.go.dev/github.com/elazarl/goproxy) package.
*/
overlay[local?]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with email-related APIs. */
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes modeling taint propagation through marshalling and encoding functions.
*/
overlay[local?]
module;
import go

View File

@@ -2,6 +2,8 @@
* Provides classes for working with remote flow sources, sinks and taint propagators
* from the `github.com/valyala/fasthttp` package.
*/
overlay[local?]
module;
import go
private import semmle.go.security.RequestForgeryCustomizations

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for modeling the `github.com/gin-gonic/gin` package.
*/
overlay[local?]
module;
import go
import semmle.go.concepts.HTTP

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for modeling the `github.com/gin-contrib/cors` package.
*/
overlay[local?]
module;
import go

Some files were not shown because too many files have changed in this diff Show More