mirror of
https://github.com/github/codeql.git
synced 2026-02-11 04:31:05 +01:00
Remove the dataflow library.
This commit is contained in:
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Provides a library for local (intra-procedural) and global (inter-procedural)
|
||||
* data flow analysis: deciding whether data can flow from a _source_ to a
|
||||
* _sink_.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing. To track these types of flow, where the
|
||||
* exact value may not be preserved, import
|
||||
* `semmle.code.cpp.dataflow.TaintTracking`.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
* (intraprocedural) data flow between expressions, call
|
||||
* `DataFlow::localExprFlow`. For more general cases of local data flow, call
|
||||
* `DataFlow::localFlow` or `DataFlow::localFlowStep` with arguments of type
|
||||
* `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import ql
|
||||
|
||||
module DataFlow {
|
||||
import internal.DataFlowImpl
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
private import ql
|
||||
private import DataFlowUtil
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
DataFlowCallable viableCallable(Call call) { result.asPredicate() = call.getTarget() }
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(Call call, DataFlowCallable f) { none() }
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
DataFlowCallable viableImplInCallContext(Call call, Call ctx) { none() }
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Provides QL-specific definitions for use in the data flow library.
|
||||
*/
|
||||
module Private {
|
||||
import DataFlowPrivate
|
||||
import DataFlowDispatch
|
||||
}
|
||||
|
||||
module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
private import ql
|
||||
private import codeql_ql.ast.internal.Builtins
|
||||
private import DataFlowUtil
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Arguments that are wrapped in an implicit varargs array
|
||||
* creation are not included, but the implicitly created array is.
|
||||
* Instance arguments are also included.
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() { exists(Argument arg | this.asExpr() = arg) }
|
||||
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
predicate argumentOf(DataFlowCall call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg | call = arg.getCall() and pos = arg.getPosition())
|
||||
}
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/** A `ReturnNode` that occurs as the result of a `ReturnStmt`. */
|
||||
private class NormalReturnNode extends ReturnNode, ExprNode {
|
||||
NormalReturnNode() { this.getExpr() instanceof ResultAccess }
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
override ReturnKind getKind() { result = TNormalReturnKind() }
|
||||
}
|
||||
|
||||
private class ExprOutNode extends OutNode, ExprNode {
|
||||
Call call;
|
||||
|
||||
ExprOutNode() { call = this.getExpr() }
|
||||
|
||||
/** Gets the underlying call. */
|
||||
override DataFlowCall getCall() { result = call }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that can read the value returned from `call` with return kind
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result = call.getNode() and
|
||||
kind = TNormalReturnKind()
|
||||
or
|
||||
result.(ArgumentOutNode).getCall() = call and
|
||||
kind = TParameterOutKind(result.(ArgumentOutNode).getIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
predicate clearsContent(Node n, Content c) {
|
||||
none() // stub implementation
|
||||
}
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
Type getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
result instanceof IntClass // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getNodeType`. */
|
||||
string ppReprType(Type t) { none() } // stub implementation
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(Type t1, Type t2) {
|
||||
any() // stub implementation
|
||||
}
|
||||
|
||||
private predicate suppressUnusedNode(Node n) { any() }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
class DataFlowType = Type;
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends Expr instanceof Call {
|
||||
/**
|
||||
* Gets the nth argument for this call.
|
||||
*
|
||||
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
|
||||
*/
|
||||
Expr getArgument(int n) { result = super.getArgument(n) }
|
||||
|
||||
/** Gets the data flow node corresponding to this call. */
|
||||
ExprNode getNode() { result.getExpr() = this }
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
DataFlowCallable getEnclosingCallable() { result.asPredicate() = this.getEnclosingPredicate() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) { none() }
|
||||
|
||||
/** The unit type. */
|
||||
private newtype TUnit = TMkUnit()
|
||||
|
||||
/** The trivial type with a single element. */
|
||||
class Unit extends TUnit {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "unit" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
|
||||
* modified or its modification cannot be observed, for example if it is a
|
||||
* freshly created object that is not saved in a variable.
|
||||
*
|
||||
* This predicate is only used for consistency checks.
|
||||
*/
|
||||
predicate isImmutableOrUnobservable(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { none() }
|
||||
|
||||
class LambdaCallKind = Unit;
|
||||
|
||||
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
|
||||
|
||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
@@ -1,513 +0,0 @@
|
||||
/**
|
||||
* Provides QL-specific definitions for use in the data flow library.
|
||||
*/
|
||||
|
||||
private import ql
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) or
|
||||
TOutParameterNode(Parameter p) or
|
||||
TArgumentOutNode(VarAccess acc, Call call, int pos) {
|
||||
acc.(Argument).getCall() = call and acc.(Argument).getPosition() = pos
|
||||
} or
|
||||
TParameterNode(Parameter p)
|
||||
|
||||
/** An argument to a call. */
|
||||
class Argument extends Expr {
|
||||
Call call;
|
||||
int pos;
|
||||
|
||||
Argument() { call.getArgument(pos) = this }
|
||||
|
||||
/** Gets the call that has this argument. */
|
||||
Call getCall() { result = call }
|
||||
|
||||
/** Gets the position of this argument. */
|
||||
int getPosition() { result = pos }
|
||||
}
|
||||
|
||||
newtype TDataFlowCallable =
|
||||
TDataFlowPredicate(Predicate p) or
|
||||
TDataFlowTopLevel() or
|
||||
TDataFlowNewtypeBranch(NewTypeBranch branch)
|
||||
|
||||
class DataFlowCallable extends TDataFlowCallable {
|
||||
string toString() {
|
||||
exists(Predicate p |
|
||||
this = TDataFlowPredicate(p) and
|
||||
result = p.toString()
|
||||
)
|
||||
or
|
||||
this = TDataFlowTopLevel() and
|
||||
result = "top level"
|
||||
or
|
||||
exists(NewTypeBranch branch |
|
||||
this = TDataFlowNewtypeBranch(branch) and
|
||||
result = branch.toString()
|
||||
)
|
||||
}
|
||||
|
||||
Predicate asPredicate() { this = TDataFlowPredicate(result) }
|
||||
|
||||
predicate asTopLevel() { this = TDataFlowTopLevel() }
|
||||
|
||||
NewTypeBranch asNewTypeBranch() { this = TDataFlowNewtypeBranch(result) }
|
||||
}
|
||||
|
||||
private newtype TParameter =
|
||||
TThisParam(ClassPredicate p) or
|
||||
TResultParam(Predicate p) { exists(p.getReturnType()) } or
|
||||
TVarParam(VarDecl v, int i, Predicate pred) { pred.getParameter(i) = v }
|
||||
|
||||
class Parameter extends TParameter {
|
||||
string toString() { this.hasName(result) }
|
||||
|
||||
predicate hasName(string name) {
|
||||
this instanceof TThisParam and name = "this"
|
||||
or
|
||||
this instanceof TResultParam and name = "result"
|
||||
or
|
||||
exists(VarDecl v | this = TVarParam(v, _, _) and name = v.toString())
|
||||
}
|
||||
|
||||
int getIndex() {
|
||||
this instanceof TThisParam and result = -1
|
||||
or
|
||||
this instanceof TResultParam and result = -2
|
||||
or
|
||||
this = TVarParam(_, result, _)
|
||||
}
|
||||
|
||||
Predicate getPredicate() {
|
||||
this = TThisParam(result)
|
||||
or
|
||||
this = TResultParam(result)
|
||||
or
|
||||
this = TVarParam(_, _, result)
|
||||
}
|
||||
|
||||
Type getType() {
|
||||
exists(ClassPredicate cp |
|
||||
this = TThisParam(cp) and
|
||||
result = cp.getDeclaringType()
|
||||
)
|
||||
or
|
||||
exists(Predicate p |
|
||||
this = TResultParam(p) and
|
||||
result = p.getReturnType()
|
||||
)
|
||||
or
|
||||
exists(VarDecl v |
|
||||
this = TVarParam(v, _, _) and
|
||||
result = v.getType()
|
||||
)
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
exists(ClassPredicate cp |
|
||||
this = TThisParam(cp) and
|
||||
result = cp.getLocation()
|
||||
)
|
||||
or
|
||||
exists(Predicate p |
|
||||
this = TResultParam(p) and
|
||||
result = p.getLocation()
|
||||
)
|
||||
or
|
||||
exists(VarDecl v |
|
||||
this = TVarParam(v, _, _) and
|
||||
result = v.getLocation()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in a data flow graph.
|
||||
*
|
||||
* A node can be either an expression, a parameter, or an uninitialized local
|
||||
* variable. Such nodes are created with `DataFlow::exprNode`,
|
||||
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets the function to which this node belongs. */
|
||||
DataFlowCallable getFunction() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||
*/
|
||||
final DataFlowCallable getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. This predicate
|
||||
* only has a result on nodes that represent the value of evaluating the
|
||||
* expression. For data flowing _out of_ an expression, like when an
|
||||
* argument is passed by reference, use `asDefiningArgument` instead of
|
||||
* `asExpr`.
|
||||
*/
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(ParameterNode).getParameter() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() } // overridden by subclasses
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Type getTypeBound() { result = getType() }
|
||||
}
|
||||
|
||||
NewTypeBranch getNewTypeBranch(Expr e) { e.getParent*() = result.getBody() }
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
|
||||
override DataFlowCallable getFunction() {
|
||||
result.asPredicate() = expr.getEnclosingPredicate()
|
||||
or
|
||||
result.asNewTypeBranch() = getNewTypeBranch(expr)
|
||||
or
|
||||
not exists(expr.getEnclosingPredicate()) and
|
||||
not exists(getNewTypeBranch(expr)) and
|
||||
result.asTopLevel()
|
||||
}
|
||||
|
||||
override Type getType() { result = expr.getType() }
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
|
||||
class ParameterNode extends Node, TParameterNode {
|
||||
Parameter p;
|
||||
|
||||
ParameterNode() { this = TParameterNode(p) }
|
||||
|
||||
Parameter getParameter() { result = p }
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
predicate isParameterOf(DataFlowCallable pred, int i) {
|
||||
p.getPredicate() = pred.asPredicate() and
|
||||
p.getIndex() = i
|
||||
}
|
||||
|
||||
override DataFlowCallable getFunction() { result.asPredicate() = p.getPredicate() }
|
||||
|
||||
override Type getType() { result = p.getType() }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
|
||||
override Location getLocation() { result = p.getLocation() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents a returned value in the called function. */
|
||||
abstract class ReturnNode extends Node {
|
||||
/** Gets the kind of this returned value. */
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
|
||||
newtype TReturnKind =
|
||||
TNormalReturnKind() or
|
||||
TParameterOutKind(int i) { any(Parameter p).getIndex() = i }
|
||||
|
||||
/**
|
||||
* A return kind. A return kind describes how a value can be returned
|
||||
* from a callable. For C++, this is simply a function return.
|
||||
*/
|
||||
class ReturnKind extends TReturnKind {
|
||||
/** Gets a textual representation of this return kind. */
|
||||
string toString() {
|
||||
this instanceof TNormalReturnKind and
|
||||
result = "return"
|
||||
or
|
||||
exists(int i |
|
||||
this = TParameterOutKind(i) and
|
||||
result = "out(" + i + ")"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call at the call site. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call. */
|
||||
abstract Call getCall();
|
||||
}
|
||||
|
||||
class ArgumentOutNode extends Node, TArgumentOutNode, OutNode {
|
||||
VarAccess acc;
|
||||
Call call;
|
||||
int pos;
|
||||
|
||||
ArgumentOutNode() { this = TArgumentOutNode(acc, call, pos) }
|
||||
|
||||
override DataFlowCallable getFunction() {
|
||||
result.asPredicate() = acc.getEnclosingPredicate()
|
||||
or
|
||||
result.asNewTypeBranch() = getNewTypeBranch(acc)
|
||||
or
|
||||
not exists(acc.getEnclosingPredicate()) and
|
||||
not exists(getNewTypeBranch(acc)) and
|
||||
result.asTopLevel()
|
||||
}
|
||||
|
||||
VarAccess getVarAccess() { result = acc }
|
||||
|
||||
override Type getType() { result = acc.getType() }
|
||||
|
||||
override string toString() { result = acc.toString() + " [out]" }
|
||||
|
||||
override Location getLocation() { result = acc.getLocation() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
|
||||
int getIndex() { result = pos }
|
||||
}
|
||||
|
||||
class OutParameterNode extends Node, ReturnNode, TOutParameterNode {
|
||||
Parameter p;
|
||||
|
||||
OutParameterNode() { this = TOutParameterNode(p) }
|
||||
|
||||
Parameter getParameter() { result = p }
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
* position. The implicit `this` parameter is considered to have index `-1`.
|
||||
*/
|
||||
predicate isParameterOf(DataFlowCallable pred, int i) {
|
||||
p.getPredicate() = pred.asPredicate() and
|
||||
p.getIndex() = i
|
||||
}
|
||||
|
||||
override DataFlowCallable getFunction() { result.asPredicate() = p.getPredicate() }
|
||||
|
||||
override Type getType() { result = p.getType() }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
|
||||
override Location getLocation() { result = p.getLocation() }
|
||||
|
||||
override ReturnKind getKind() { result = TParameterOutKind(p.getIndex()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node associated with an object after an operation that might have
|
||||
* changed its state.
|
||||
*
|
||||
* This can be either the argument to a callable after the callable returns
|
||||
* (which might have mutated the argument), or the qualifier of a field after
|
||||
* an update to the field.
|
||||
*
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
|
||||
override DataFlowCallable getFunction() { result = getPreUpdateNode().getFunction() }
|
||||
|
||||
override Type getType() { result = getPreUpdateNode().getType() }
|
||||
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
|
||||
|
||||
AstNode getParentOfExpr(Expr e) { result = e.getParent() }
|
||||
|
||||
Formula getEnclosing(Expr e) { result = getParentOfExpr+(e) }
|
||||
|
||||
Formula enlargeScopeStep(Formula f) { result.(Conjunction).getAnOperand() = f }
|
||||
|
||||
Formula enlargeScope(Formula f) {
|
||||
result = enlargeScopeStep*(f) and not exists(enlargeScopeStep(result))
|
||||
}
|
||||
|
||||
predicate varaccesValue(VarAccess va, VarDecl v, Formula scope) {
|
||||
va.getDeclaration() = v and
|
||||
scope = enlargeScope(getEnclosing(va))
|
||||
}
|
||||
|
||||
predicate thisValue(ThisAccess ta, Formula scope) { scope = enlargeScope(getEnclosing(ta)) }
|
||||
|
||||
predicate resultValue(ResultAccess ra, Formula scope) { scope = enlargeScope(getEnclosing(ra)) }
|
||||
|
||||
Formula getParentFormula(Formula f) { f.getParent() = result }
|
||||
|
||||
predicate valueStep(Expr e1, Expr e2) {
|
||||
exists(VarDecl v, Formula scope |
|
||||
varaccesValue(e1, v, scope) and
|
||||
varaccesValue(e2, v, scope)
|
||||
)
|
||||
or
|
||||
exists(VarDecl v, Formula f, Select sel |
|
||||
getParentFormula*(f) = sel.getWhere() and
|
||||
varaccesValue(e1, v, f) and
|
||||
sel.getExpr(_) = e2
|
||||
)
|
||||
or
|
||||
exists(Formula scope |
|
||||
thisValue(e1, scope) and
|
||||
thisValue(e2, scope)
|
||||
or
|
||||
resultValue(e1, scope) and
|
||||
resultValue(e2, scope)
|
||||
)
|
||||
or
|
||||
exists(InlineCast c |
|
||||
e1 = c and e2 = c.getBase()
|
||||
or
|
||||
e2 = c and e1 = c.getBase()
|
||||
)
|
||||
or
|
||||
exists(ComparisonFormula eq |
|
||||
eq.getSymbol() = "=" and
|
||||
eq.getAnOperand() = e1 and
|
||||
eq.getAnOperand() = e2 and
|
||||
e1 != e2
|
||||
)
|
||||
}
|
||||
|
||||
predicate paramStep(Expr e1, Parameter p2) {
|
||||
exists(VarDecl v |
|
||||
p2 = TVarParam(v, _, _) and
|
||||
varaccesValue(e1, v, _)
|
||||
)
|
||||
or
|
||||
exists(Formula scope |
|
||||
p2 = TThisParam(scope.getEnclosingPredicate()) and
|
||||
thisValue(e1, scope)
|
||||
or
|
||||
p2 = TResultParam(scope.getEnclosingPredicate()) and
|
||||
resultValue(e1, scope)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
valueStep(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
paramStep(nodeFrom.asExpr(), nodeTo.(OutParameterNode).getParameter())
|
||||
or
|
||||
valueStep(nodeFrom.(ArgumentOutNode).getVarAccess(), nodeTo.asExpr())
|
||||
}
|
||||
|
||||
predicate foo(Node nodeFrom, Node nodeTo, Select sel) {
|
||||
valueStep(nodeFrom.(ArgumentOutNode).getVarAccess(), nodeTo.asExpr()) and
|
||||
sel.getExpr(_) = nodeTo.asExpr() and
|
||||
nodeFrom.getLocation().getFile().getBaseName() = "OverflowStatic.ql" and
|
||||
nodeFrom.getLocation().getStartLine() = 147
|
||||
// paramStep(nodeFrom.asExpr(), nodeTo.(OutParameterNode).getParameter()) and
|
||||
// nodeFrom.getLocation().getStartLine() = 60 and
|
||||
// nodeFrom.getLocation().getFile().getBaseName() = "OverflowStatic.ql"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent() or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
|
||||
/**
|
||||
* A description of the way data may be stored inside an object. Examples
|
||||
* include instance fields, the contents of a collection object, or the contents
|
||||
* of an array.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
}
|
||||
|
||||
/** A reference through an instance field. */
|
||||
class FieldContent extends Content, TFieldContent {
|
||||
override string toString() { result = "<field>" }
|
||||
}
|
||||
|
||||
/** A reference through an array. */
|
||||
private class ArrayContent extends Content, TArrayContent {
|
||||
override string toString() { result = "[]" }
|
||||
}
|
||||
|
||||
/** A reference through the contents of some collection-like container. */
|
||||
private class CollectionContent extends Content, TCollectionContent {
|
||||
override string toString() { result = "<element>" }
|
||||
}
|
||||
|
||||
class GuardCondition extends Formula {
|
||||
GuardCondition() { any(IfFormula ifFormula).getCondition() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard that validates some expression.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
* `checks` to specify what is being validated and in which branch.
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final ExprNode getAGuardedNode() { none() }
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
import ql
|
||||
import codeql_ql.ast.internal.Builtins
|
||||
import codeql.dataflow.DataFlow
|
||||
|
||||
class ToString extends Predicate {
|
||||
ToString() { this.getName() = "toString" }
|
||||
@@ -36,25 +35,209 @@ class RegexpReplaceAllCall extends MemberCall {
|
||||
RegexpReplaceAllCall() { this.getTarget() instanceof RegexpReplaceAll }
|
||||
}
|
||||
|
||||
class ToSelectConf extends DataFlow::Configuration {
|
||||
ToSelectConf() { this = "ToSelectConf" }
|
||||
module DataFlow {
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) or
|
||||
TOutParameterNode(Parameter p) or
|
||||
TArgumentOutNode(VarAccess acc, Call call, int pos) {
|
||||
acc.(Argument).getCall() = call and acc.(Argument).getPosition() = pos
|
||||
} or
|
||||
TParameterNode(Parameter p)
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(ToStringCall toString |
|
||||
source.asExpr() = toString and
|
||||
not toString.getEnclosingPredicate() instanceof ToString
|
||||
/** An argument to a call. */
|
||||
class Argument extends Expr {
|
||||
Call call;
|
||||
int pos;
|
||||
|
||||
Argument() { call.getArgument(pos) = this }
|
||||
|
||||
/** Gets the call that has this argument. */
|
||||
Call getCall() { result = call }
|
||||
|
||||
/** Gets the position of this argument. */
|
||||
int getPosition() { result = pos }
|
||||
}
|
||||
|
||||
private newtype TParameter =
|
||||
TThisParam(ClassPredicate p) or
|
||||
TResultParam(Predicate p) { exists(p.getReturnType()) } or
|
||||
TVarParam(VarDecl v, int i, Predicate pred) { pred.getParameter(i) = v }
|
||||
|
||||
class Parameter extends TParameter {
|
||||
string toString() { this.hasName(result) }
|
||||
|
||||
ClassPredicate asThis() { this = TThisParam(result) }
|
||||
|
||||
Predicate asResult() { this = TResultParam(result) }
|
||||
|
||||
VarDecl asVar(int i, Predicate pred) { this = TVarParam(result, i, pred) }
|
||||
|
||||
predicate hasName(string name) {
|
||||
exists(this.asThis()) and name = "this"
|
||||
or
|
||||
exists(this.asResult()) and name = "result"
|
||||
or
|
||||
name = this.asVar(_, _).getName()
|
||||
}
|
||||
|
||||
int getIndex() {
|
||||
exists(this.asThis()) and result = -1
|
||||
or
|
||||
exists(this.asResult()) and result = -2
|
||||
or
|
||||
exists(this.asVar(result, _))
|
||||
}
|
||||
|
||||
Predicate getPredicate() {
|
||||
result = this.asThis()
|
||||
or
|
||||
result = this.asResult()
|
||||
or
|
||||
exists(this.asVar(_, result))
|
||||
}
|
||||
}
|
||||
|
||||
class Node extends TNode {
|
||||
/** Gets the predicate to which this node belongs. */
|
||||
Predicate getPredicate() { none() } // overridden in subclasses
|
||||
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() } // overridden by subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
|
||||
override string toString() { result = expr.toString() }
|
||||
|
||||
/** Gets the expression corresponding to this node. */
|
||||
Expr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
class ParameterNode extends Node, TParameterNode {
|
||||
Parameter p;
|
||||
|
||||
ParameterNode() { this = TParameterNode(p) }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
}
|
||||
|
||||
newtype TReturnKind =
|
||||
TNormalReturnKind() or
|
||||
TParameterOutKind(int i) { any(Parameter p).getIndex() = i }
|
||||
|
||||
/** A data flow node that represents the output of a call at the call site. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call. */
|
||||
abstract Call getCall();
|
||||
}
|
||||
|
||||
class ArgumentOutNode extends Node, TArgumentOutNode, OutNode {
|
||||
VarAccess acc;
|
||||
Call call;
|
||||
int pos;
|
||||
|
||||
ArgumentOutNode() { this = TArgumentOutNode(acc, call, pos) }
|
||||
|
||||
VarAccess getVarAccess() { result = acc }
|
||||
|
||||
override string toString() { result = acc.toString() + " [out]" }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
|
||||
int getIndex() { result = pos }
|
||||
}
|
||||
|
||||
class OutParameterNode extends Node, TOutParameterNode {
|
||||
Parameter p;
|
||||
|
||||
OutParameterNode() { this = TOutParameterNode(p) }
|
||||
|
||||
Parameter getParameter() { result = p }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
}
|
||||
|
||||
AstNode getParentOfExpr(Expr e) { result = e.getParent() }
|
||||
|
||||
Formula getEnclosing(Expr e) { result = getParentOfExpr+(e) }
|
||||
|
||||
Formula enlargeScopeStep(Formula f) { result.(Conjunction).getAnOperand() = f }
|
||||
|
||||
Formula enlargeScope(Formula f) {
|
||||
result = enlargeScopeStep*(f) and not exists(enlargeScopeStep(result))
|
||||
}
|
||||
|
||||
predicate varaccesValue(VarAccess va, VarDecl v, Formula scope) {
|
||||
va.getDeclaration() = v and
|
||||
scope = enlargeScope(getEnclosing(va))
|
||||
}
|
||||
|
||||
predicate thisValue(ThisAccess ta, Formula scope) { scope = enlargeScope(getEnclosing(ta)) }
|
||||
|
||||
predicate resultValue(ResultAccess ra, Formula scope) { scope = enlargeScope(getEnclosing(ra)) }
|
||||
|
||||
Formula getParentFormula(Formula f) { f.getParent() = result }
|
||||
|
||||
predicate valueStep(Expr e1, Expr e2) {
|
||||
exists(VarDecl v, Formula scope |
|
||||
varaccesValue(e1, v, scope) and
|
||||
varaccesValue(e2, v, scope)
|
||||
)
|
||||
or
|
||||
exists(VarDecl v, Formula f, Select sel |
|
||||
getParentFormula*(f) = sel.getWhere() and
|
||||
varaccesValue(e1, v, f) and
|
||||
sel.getExpr(_) = e2
|
||||
)
|
||||
or
|
||||
exists(Formula scope |
|
||||
thisValue(e1, scope) and
|
||||
thisValue(e2, scope)
|
||||
or
|
||||
resultValue(e1, scope) and
|
||||
resultValue(e2, scope)
|
||||
)
|
||||
or
|
||||
exists(InlineCast c |
|
||||
e1 = c and e2 = c.getBase()
|
||||
or
|
||||
e2 = c and e1 = c.getBase()
|
||||
)
|
||||
or
|
||||
exists(ComparisonFormula eq |
|
||||
eq.getSymbol() = "=" and
|
||||
eq.getAnOperand() = e1 and
|
||||
eq.getAnOperand() = e2 and
|
||||
e1 != e2
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(Select s).getExpr(_) or
|
||||
sink.getEnclosingCallable().asPredicate() instanceof NodesPredicate or
|
||||
sink.getEnclosingCallable().asPredicate() instanceof EdgesPredicate
|
||||
predicate paramStep(Expr e1, Parameter p2) {
|
||||
exists(VarDecl v |
|
||||
p2 = TVarParam(v, _, _) and
|
||||
varaccesValue(e1, v, _)
|
||||
)
|
||||
or
|
||||
exists(Formula scope |
|
||||
p2 = TThisParam(scope.getEnclosingPredicate()) and
|
||||
thisValue(e1, scope)
|
||||
or
|
||||
p2 = TResultParam(scope.getEnclosingPredicate()) and
|
||||
resultValue(e1, scope)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
nodeFrom.getType() instanceof StringClass and
|
||||
nodeTo.getType() instanceof StringClass and
|
||||
predicate additionalLocalStep(Node nodeFrom, Node nodeTo) {
|
||||
nodeFrom.asExpr().getType() instanceof StringClass and
|
||||
nodeFrom.asExpr().getType() instanceof StringClass and
|
||||
exists(BinOpExpr binop |
|
||||
nodeFrom.asExpr() = binop.getAnOperand() and
|
||||
nodeTo.asExpr() = binop
|
||||
@@ -62,12 +245,65 @@ class ToSelectConf extends DataFlow::Configuration {
|
||||
or
|
||||
nodeTo.asExpr().(RegexpReplaceAllCall).getBase() = nodeFrom.asExpr()
|
||||
}
|
||||
|
||||
predicate localStep(Node nodeFrom, Node nodeTo) {
|
||||
valueStep(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
paramStep(nodeFrom.asExpr(), nodeTo.(OutParameterNode).getParameter())
|
||||
or
|
||||
valueStep(nodeFrom.(ArgumentOutNode).getVarAccess(), nodeTo.asExpr())
|
||||
or
|
||||
additionalLocalStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate step(Node nodeFrom, Node nodeTo) {
|
||||
// Local flow
|
||||
localStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow out of functions
|
||||
exists(Call call, Parameter p, OutParameterNode outParam, ArgumentOutNode outArg |
|
||||
outParam = nodeFrom and
|
||||
outArg = nodeTo
|
||||
|
|
||||
p = outParam.getParameter() and
|
||||
p.getPredicate() = call.getTarget() and
|
||||
outArg.getCall() = call and
|
||||
outArg.getIndex() = p.getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
predicate flowsFromSource(Node node) {
|
||||
isSource(node)
|
||||
or
|
||||
exists(Node mid | flowsFromSource(mid) | step(mid, node))
|
||||
}
|
||||
|
||||
predicate flowsToSink(Node node) {
|
||||
flowsFromSource(node) and isSink(node)
|
||||
or
|
||||
exists(Node mid | flowsToSink(mid) | step(node, mid))
|
||||
}
|
||||
|
||||
predicate isSink(Node sink) {
|
||||
sink.asExpr() = any(Select s).getExpr(_)
|
||||
or
|
||||
sink.getPredicate() instanceof NodesPredicate
|
||||
or
|
||||
sink.getPredicate() instanceof EdgesPredicate
|
||||
}
|
||||
|
||||
predicate isSource(Node source) {
|
||||
exists(ToStringCall toString |
|
||||
source.asExpr() = toString and
|
||||
not toString.getEnclosingPredicate() instanceof ToString
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate flowsToSelect(Expr e) {
|
||||
exists(DataFlow::Node source |
|
||||
source.asExpr() = e and
|
||||
any(ToSelectConf conf).hasFlow(source, _)
|
||||
DataFlow::flowsToSink(source)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user