mirror of
https://github.com/github/codeql.git
synced 2026-04-23 07:45:17 +02:00
JS: introduce implicit this uses in general
This commit is contained in:
@@ -26,6 +26,7 @@ private import internal.AnalyzedParameters
|
||||
private import internal.PreCallGraphStep
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
private import semmle.javascript.dataflow.internal.DataFlowPrivate as Private
|
||||
private import semmle.javascript.dataflow.internal.VariableOrThis
|
||||
|
||||
module DataFlow {
|
||||
/**
|
||||
@@ -729,9 +730,7 @@ module DataFlow {
|
||||
private class ParameterFieldAsPropWrite extends PropWrite, PropNode {
|
||||
override ParameterField prop;
|
||||
|
||||
override Node getBase() {
|
||||
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
|
||||
}
|
||||
override Node getBase() { result = TImplicitThisUse(prop, false) }
|
||||
|
||||
override Expr getPropertyNameExpr() {
|
||||
none() // The parameter value is not the name of the field
|
||||
@@ -758,9 +757,7 @@ module DataFlow {
|
||||
exists(prop.getInit())
|
||||
}
|
||||
|
||||
override Node getBase() {
|
||||
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
|
||||
}
|
||||
override Node getBase() { result = TImplicitThisUse(prop, false) }
|
||||
|
||||
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
|
||||
|
||||
@@ -1045,12 +1042,12 @@ module DataFlow {
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the value passed as `this` argument in a `new` call or a `super` call.
|
||||
* A node representing the value passed as `this` argument in a `new` call.
|
||||
*/
|
||||
class ConstructorThisArgumentNode extends TConstructorThisArgumentNode, DataFlow::Node {
|
||||
private InvokeExpr expr;
|
||||
class NewCallThisArgumentNode extends TNewCallThisArgument, DataFlow::Node {
|
||||
private NewExpr expr;
|
||||
|
||||
ConstructorThisArgumentNode() { this = TConstructorThisArgumentNode(expr) }
|
||||
NewCallThisArgumentNode() { this = TNewCallThisArgument(expr) }
|
||||
|
||||
override string toString() { result = "implicit 'this' argument of " + expr }
|
||||
|
||||
@@ -1060,18 +1057,23 @@ module DataFlow {
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the post-update node corresponding to implicit uses of `this` in a constructor.
|
||||
* A node representing an implicit use of `this` or its post-update node.
|
||||
*/
|
||||
private class ConstructorThisPostUpdateNode extends TConstructorThisPostUpdate, DataFlow::Node {
|
||||
private Function constructor;
|
||||
private class ImplicitThisUseNode extends TImplicitThisUse, DataFlow::Node {
|
||||
private ImplicitThisUse use;
|
||||
private boolean isPost;
|
||||
|
||||
ConstructorThisPostUpdateNode() { this = TConstructorThisPostUpdate(constructor) }
|
||||
ImplicitThisUseNode() { this = TImplicitThisUse(use, isPost) }
|
||||
|
||||
override string toString() { result = "[post-update] 'this' parameter of " + constructor }
|
||||
override string toString() {
|
||||
if isPost = false
|
||||
then result = "implicit 'this'"
|
||||
else result = "[post-update] implicit 'this'"
|
||||
}
|
||||
|
||||
override StmtContainer getContainer() { result = constructor }
|
||||
override StmtContainer getContainer() { result = use.getUseContainer() }
|
||||
|
||||
override Location getLocation() { result = constructor.getLocation() }
|
||||
override Location getLocation() { result = use.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1682,6 +1684,12 @@ module DataFlow {
|
||||
pred = TReflectiveCallNode(call, _) and
|
||||
succ = TValueNode(call)
|
||||
)
|
||||
or
|
||||
// Pass 'this' into implicit uses of 'this'
|
||||
exists(ImplicitThisUse use |
|
||||
pred = TThisNode(use.getBindingContainer()) and
|
||||
succ = TImplicitThisUse(use, false)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1772,12 +1780,6 @@ module DataFlow {
|
||||
pred = TReflectiveParametersNode(f) and
|
||||
succ = TValueNode(f.getArgumentsVariable().getAnAccess())
|
||||
)
|
||||
or
|
||||
// Pass 'this' into super calls
|
||||
exists(SuperCall call |
|
||||
pred = TThisNode(call.getBinder()) and
|
||||
succ = TConstructorThisArgumentNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
private class ReflectiveParamsStep extends LegacyPreCallGraphStep {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import codeql.util.Boolean
|
||||
private import semmle.javascript.dataflow.internal.AdditionalFlowInternal
|
||||
private import semmle.javascript.dataflow.internal.Contents::Private
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
|
||||
@@ -13,6 +14,7 @@ private import semmle.javascript.dataflow.internal.DataFlowPrivate as DataFlowPr
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
|
||||
private import semmle.javascript.dataflow.internal.VariableCapture as VariableCapture
|
||||
private import semmle.javascript.dataflow.internal.VariableOrThis
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
@@ -31,7 +33,7 @@ private module Cached {
|
||||
/** An SSA node from the legacy SSA library */
|
||||
TSsaDefNode(SsaDefinition d) or
|
||||
/** Use of a variable or 'this', with flow from a post-update node (from an earlier use) */
|
||||
TSsaUseNode(Expr use) { use = any(Ssa2::SsaConfig::SourceVariable v).getAnAccess() } or
|
||||
TSsaUseNode(ControlFlowNode use) { use = any(Ssa2::SsaConfig::SourceVariable v).getAUse() } or
|
||||
/** Phi-read node (new SSA library). Ordinary phi nodes are represented by TSsaDefNode. */
|
||||
TSsaPhiReadNode(Ssa2::PhiReadNode phi) or
|
||||
/** Input to a phi node (new SSA library) */
|
||||
@@ -88,8 +90,8 @@ private module Cached {
|
||||
// The RHS of an assignment can be an argument to a setter-call, so it needs a post-update node
|
||||
e = any(Assignment asn | asn.getTarget() instanceof PropAccess).getRhs()
|
||||
} or
|
||||
TConstructorThisArgumentNode(InvokeExpr e) { e instanceof NewExpr or e instanceof SuperCall } or
|
||||
TConstructorThisPostUpdate(Constructor ctor) or
|
||||
TNewCallThisArgument(NewExpr e) or
|
||||
TImplicitThisUse(ImplicitThisUse use, Boolean isPost) or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
|
||||
TFlowSummaryDynamicParameterArrayNode(FlowSummaryImpl::Public::SummarizedCallable callable) or
|
||||
TFlowSummaryIntermediateAwaitStoreNode(FlowSummaryImpl::Private::SummaryNode sn) {
|
||||
@@ -130,8 +132,9 @@ private class TEarlyStageNode =
|
||||
TFunctionSelfReferenceNode or TDestructuredModuleImportNode or THtmlAttributeNode or
|
||||
TFunctionReturnNode or TExceptionalFunctionReturnNode or TExceptionalInvocationReturnNode or
|
||||
TGlobalAccessPathRoot or TTemplatePlaceholderTag or TReflectiveParametersNode or
|
||||
TExprPostUpdateNode or TConstructorThisArgumentNode or TStaticArgumentArrayNode or
|
||||
TDynamicArgumentArrayNode or TStaticParameterArrayNode or TDynamicParameterArrayNode;
|
||||
TExprPostUpdateNode or TNewCallThisArgument or TStaticArgumentArrayNode or
|
||||
TDynamicArgumentArrayNode or TStaticParameterArrayNode or TDynamicParameterArrayNode or
|
||||
TImplicitThisUse;
|
||||
|
||||
/**
|
||||
* A data-flow node that is not a flow summary node.
|
||||
|
||||
@@ -21,7 +21,7 @@ private class Node = DataFlow::Node;
|
||||
class PostUpdateNode = DataFlow::PostUpdateNode;
|
||||
|
||||
class SsaUseNode extends DataFlow::Node, TSsaUseNode {
|
||||
private Expr expr;
|
||||
private ControlFlowNode expr;
|
||||
|
||||
SsaUseNode() { this = TSsaUseNode(expr) }
|
||||
|
||||
@@ -333,18 +333,13 @@ predicate postUpdatePair(Node pre, Node post) {
|
||||
)
|
||||
or
|
||||
exists(NewExpr expr |
|
||||
pre = TConstructorThisArgumentNode(expr) and
|
||||
pre = TNewCallThisArgument(expr) and
|
||||
post = TValueNode(expr)
|
||||
)
|
||||
or
|
||||
exists(SuperCall expr |
|
||||
pre = TConstructorThisArgumentNode(expr) and
|
||||
post = TConstructorThisPostUpdate(expr.getBinder())
|
||||
)
|
||||
or
|
||||
exists(Function constructor |
|
||||
pre = TThisNode(constructor) and
|
||||
post = TConstructorThisPostUpdate(constructor)
|
||||
exists(ImplicitThisUse use |
|
||||
pre = TImplicitThisUse(use, false) and
|
||||
post = TImplicitThisUse(use, true)
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::summaryPostUpdateNode(post.(FlowSummaryNode).getSummaryNode(),
|
||||
@@ -473,7 +468,7 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
|
||||
pos.isThis()
|
||||
)
|
||||
or
|
||||
pos.isThis() and n = TConstructorThisArgumentNode(call.asOrdinaryCall().asExpr())
|
||||
pos.isThis() and n = TNewCallThisArgument(call.asOrdinaryCall().asExpr())
|
||||
or
|
||||
// receiver of accessor call
|
||||
pos.isThis() and n = call.asAccessorCall().getBase()
|
||||
@@ -1068,6 +1063,11 @@ private Node getNodeFromSsa2(Ssa2::Node node) {
|
||||
or
|
||||
result = TExprPostUpdateNode(node.(Ssa2::ExprPostUpdateNode).getExpr())
|
||||
or
|
||||
exists(ImplicitThisUse use |
|
||||
node.(Ssa2::ExprPostUpdateNode).getExpr() = use and
|
||||
result = TImplicitThisUse(use, true)
|
||||
)
|
||||
or
|
||||
result = TSsaPhiReadNode(node.(Ssa2::SsaDefinitionExtNode).getDefinitionExt())
|
||||
or
|
||||
result = TSsaInputNode(node.(Ssa2::SsaInputNode))
|
||||
@@ -1091,6 +1091,11 @@ private predicate useUseFlow(Node node1, Node node2) {
|
||||
node1 = TSsaUseNode(use) and
|
||||
node2 = TValueNode(use)
|
||||
)
|
||||
or
|
||||
exists(ImplicitThisUse use |
|
||||
node1 = TSsaUseNode(use) and
|
||||
node2 = TImplicitThisUse(use, false)
|
||||
)
|
||||
}
|
||||
|
||||
predicate simpleLocalFlowStep(Node node1, Node node2, string model) {
|
||||
@@ -1298,11 +1303,16 @@ private Node getPostUpdateForStore(Node base) {
|
||||
expr instanceof VarAccess or
|
||||
expr instanceof ThisExpr
|
||||
)
|
||||
or
|
||||
exists(ImplicitThisUse use |
|
||||
base = TImplicitThisUse(use, false) and
|
||||
result = TImplicitThisUse(use, true)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets node to target with a store to the given `base` object.. */
|
||||
pragma[inline]
|
||||
private Node getStoreTarget(Node base) {
|
||||
private Node getStoreTarget(DataFlow::Node base) {
|
||||
result = getPostUpdateForStore(base)
|
||||
or
|
||||
not exists(getPostUpdateForStore(base)) and
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import javascript
|
||||
private import DataFlowNode
|
||||
|
||||
cached
|
||||
private newtype TLocalVariableOrThis =
|
||||
@@ -44,14 +45,17 @@ class LocalVariableOrThis extends TLocalVariableOrThis {
|
||||
result = this.asThisContainer()
|
||||
}
|
||||
|
||||
/** Gets an access to `this` represented by this value. */
|
||||
ThisExpr getAThisAccess() { result.getBindingContainer() = this.asThisContainer() }
|
||||
/** Gets an explicit access to `this` represented by this value. */
|
||||
ThisExpr getAThisExpr() { result.getBindingContainer() = this.asThisContainer() }
|
||||
|
||||
/** Gets an access to variable or `this`. */
|
||||
Expr getAnAccess() {
|
||||
/** Gets an implicit or explicit use of the `this` represented by this value. */
|
||||
ThisUse getAThisUse() { result.getBindingContainer() = this.asThisContainer() }
|
||||
|
||||
/** Gets an expression that accesses this variable or `this`. */
|
||||
ControlFlowNode getAUse() {
|
||||
result = this.asLocalVariable().getAnAccess()
|
||||
or
|
||||
result = this.getAThisAccess()
|
||||
result = this.getAThisUse()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,3 +78,50 @@ module LocalVariableOrThis {
|
||||
/** Gets the representation of `this` in the given container. */
|
||||
LocalVariableOrThis thisInContainer(StmtContainer c) { result = TThis(c) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An explicit or implicit use of `this`.
|
||||
*
|
||||
* Implicit uses include `super()` calls and instance field initializers (which includes TypeScript parameter fields).
|
||||
*/
|
||||
abstract class ThisUse instanceof ControlFlowNode {
|
||||
/** Gets the container binding the `this` being accessed */
|
||||
abstract StmtContainer getBindingContainer();
|
||||
|
||||
abstract StmtContainer getUseContainer();
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
DbLocation getLocation() { result = super.getLocation() }
|
||||
}
|
||||
|
||||
private predicate implicitThisUse(ControlFlowNode node, StmtContainer thisBinder) {
|
||||
thisBinder = node.(SuperExpr).getBinder()
|
||||
or
|
||||
exists(FieldDefinition field |
|
||||
not field.isStatic() and
|
||||
node = field and
|
||||
thisBinder = field.getDeclaringClass().getConstructor().getBody()
|
||||
)
|
||||
}
|
||||
|
||||
class ImplicitThisUse extends ThisUse {
|
||||
ImplicitThisUse() { implicitThisUse(this, _) }
|
||||
|
||||
override StmtContainer getBindingContainer() { implicitThisUse(this, result) }
|
||||
|
||||
override StmtContainer getUseContainer() {
|
||||
// The following differs from FieldDefinition.getContainer() which returns the container enclosing
|
||||
// the class, not the class constructor.
|
||||
// TODO: consider changing this in FieldDefinition.getContainer()
|
||||
result = this.(FieldDefinition).getDeclaringClass().getConstructor().getBody()
|
||||
or
|
||||
result = this.(SuperExpr).getContainer()
|
||||
}
|
||||
}
|
||||
|
||||
private class ExplicitThisUse extends ThisUse instanceof ThisExpr {
|
||||
override StmtContainer getBindingContainer() { result = ThisExpr.super.getBindingContainer() }
|
||||
|
||||
override StmtContainer getUseContainer() { result = ThisExpr.super.getContainer() }
|
||||
}
|
||||
|
||||
@@ -42,8 +42,7 @@ module SsaConfig implements InputSig<js::DbLocation> {
|
||||
bb.useAt(i, v.asLocalVariable(), _) and certain = true
|
||||
or
|
||||
certain = true and
|
||||
bb.getNode(i) = v.getAThisAccess()
|
||||
// TODO: also account for: super() and field initialisers
|
||||
bb.getNode(i).(ThisUse).getBindingContainer() = v.asThisContainer()
|
||||
}
|
||||
|
||||
predicate getImmediateBasicBlockDominator = BasicBlockInternal::immediateDominator/1;
|
||||
@@ -55,8 +54,8 @@ module SsaConfig implements InputSig<js::DbLocation> {
|
||||
import Make<js::DbLocation, SsaConfig>
|
||||
|
||||
private module SsaDataflowInput implements DataFlowIntegrationInputSig {
|
||||
class Expr extends js::Expr {
|
||||
Expr() { this = any(SsaConfig::SourceVariable v).getAnAccess() }
|
||||
class Expr extends js::ControlFlowNode {
|
||||
Expr() { this = any(SsaConfig::SourceVariable v).getAUse() }
|
||||
|
||||
predicate hasCfgNode(js::BasicBlock bb, int i) { this = bb.getNode(i) }
|
||||
}
|
||||
|
||||
@@ -60,5 +60,5 @@ function t5() {
|
||||
}
|
||||
}
|
||||
const c = new C();
|
||||
sink(c.field); // $ MISSING: hasValueFlow=t5.1
|
||||
sink(c.field); // $ hasValueFlow=t5.1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user