Skip to content

Commit 7fbc730

Browse files
authored
Merge pull request #1517 from asger-semmle/instance-type-tracking-final
Approved by xiemaisi
2 parents 44823ca + 329ff0d commit 7fbc730

File tree

4 files changed

+127
-11
lines changed

4 files changed

+127
-11
lines changed

javascript/ql/src/semmle/javascript/dataflow/LocalObjects.qll

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,31 @@ class LocalObject extends DataFlow::SourceNode {
6767
not exposedAsReceiver(this)
6868
}
6969

70+
pragma[nomagic]
7071
predicate hasOwnProperty(string name) {
7172
// the property is defined in the initializer,
7273
any(DataFlow::PropWrite write).writes(this, name, _) and
7374
// and it is never deleted
74-
not exists(DeleteExpr del, DataFlow::PropRef ref |
75+
not hasDeleteWithName(name) and
76+
// and there is no deleted property with computed name
77+
not hasDeleteWithComputedProperty()
78+
}
79+
80+
pragma[noinline]
81+
private predicate hasDeleteWithName(string name) {
82+
exists(DeleteExpr del, DataFlow::PropRef ref |
83+
del.getOperand().flow() = ref and
84+
flowsTo(ref.getBase()) and
85+
ref.getPropertyName() = name
86+
)
87+
}
88+
89+
pragma[noinline]
90+
private predicate hasDeleteWithComputedProperty() {
91+
exists(DeleteExpr del, DataFlow::PropRef ref |
7592
del.getOperand().flow() = ref and
7693
flowsTo(ref.getBase()) and
77-
(ref.getPropertyName() = name or not exists(ref.getPropertyName()))
94+
not exists(ref.getPropertyName())
7895
)
7996
}
8097
}

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 78 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -642,16 +642,23 @@ class ClassNode extends DataFlow::SourceNode {
642642
*/
643643
FunctionNode getAStaticMethod() { result = impl.getAStaticMethod() }
644644

645+
/**
646+
* Gets a dataflow node that refers to the superclass of this class.
647+
*/
648+
DataFlow::Node getASuperClassNode() { result = impl.getASuperClassNode() }
649+
645650
/**
646651
* Gets a direct super class of this class.
647652
*/
648653
ClassNode getADirectSuperClass() {
649-
result.getConstructor().getAstNode() = impl
650-
.getASuperClassNode()
651-
.analyze()
652-
.getAValue()
653-
.(AbstractCallable)
654-
.getFunction()
654+
result.getAClassReference().flowsTo(getASuperClassNode())
655+
}
656+
657+
/**
658+
* Gets a direct subclass of this class.
659+
*/
660+
final ClassNode getADirectSubClass() {
661+
this = result.getADirectSuperClass()
655662
}
656663

657664
/**
@@ -662,6 +669,63 @@ class ClassNode extends DataFlow::SourceNode {
662669
or
663670
result = getAnInstanceMember().getReceiver()
664671
}
672+
673+
/**
674+
* Gets the abstract value representing the class itself.
675+
*/
676+
AbstractValue getAbstractClassValue() {
677+
result = this.(AnalyzedNode).getAValue()
678+
}
679+
680+
/**
681+
* Gets the abstract value representing an instance of this class.
682+
*/
683+
AbstractValue getAbstractInstanceValue() {
684+
result = AbstractInstance::of(getAstNode())
685+
}
686+
687+
/**
688+
* Gets a dataflow node that refers to this class object.
689+
*/
690+
private DataFlow::SourceNode getAClassReference(DataFlow::TypeTracker t) {
691+
t.start() and
692+
result.(AnalyzedNode).getAValue() = getAbstractClassValue()
693+
or
694+
exists(DataFlow::TypeTracker t2 |
695+
result = getAClassReference(t2).track(t2, t)
696+
)
697+
}
698+
699+
/**
700+
* Gets a dataflow node that refers to this class object.
701+
*/
702+
DataFlow::SourceNode getAClassReference() {
703+
result = getAClassReference(DataFlow::TypeTracker::end())
704+
}
705+
706+
/**
707+
* Gets a dataflow node that refers to an instance of this class.
708+
*/
709+
private DataFlow::SourceNode getAnInstanceReference(DataFlow::TypeTracker t) {
710+
result = getAClassReference(t.continue()).getAnInstantiation()
711+
or
712+
t.start() and
713+
result.(AnalyzedNode).getAValue() = getAbstractInstanceValue()
714+
or
715+
t.start() and
716+
result = getAReceiverNode()
717+
or
718+
exists(DataFlow::TypeTracker t2 |
719+
result = getAnInstanceReference(t2).track(t2, t)
720+
)
721+
}
722+
723+
/**
724+
* Gets a dataflow node that refers to an instance of this class.
725+
*/
726+
DataFlow::SourceNode getAnInstanceReference() {
727+
result = getAnInstanceReference(DataFlow::TypeTracker::end())
728+
}
665729
}
666730

667731
module ClassNode {
@@ -803,6 +867,9 @@ module ClassNode {
803867
kind = MemberKind::method() and
804868
result = getAPrototypeReference().getAPropertySource(name)
805869
or
870+
kind = MemberKind::method() and
871+
result = getConstructor().getReceiver().getAPropertySource(name)
872+
or
806873
exists(PropertyAccessor accessor |
807874
accessor = getAnAccessor(kind) and
808875
accessor.getName() = name and
@@ -812,7 +879,10 @@ module ClassNode {
812879

813880
override FunctionNode getAnInstanceMember(MemberKind kind) {
814881
kind = MemberKind::method() and
815-
result = getAPrototypeReference().getAPropertyWrite().getRhs().getALocalSource()
882+
result = getAPrototypeReference().getAPropertySource()
883+
or
884+
kind = MemberKind::method() and
885+
result = getConstructor().getReceiver().getAPropertySource()
816886
or
817887
exists(PropertyAccessor accessor |
818888
accessor = getAnAccessor(kind) and
@@ -823,7 +893,7 @@ module ClassNode {
823893
override FunctionNode getStaticMethod(string name) { result = getAPropertySource(name) }
824894

825895
override FunctionNode getAStaticMethod() {
826-
result = getAPropertyWrite().getRhs().getALocalSource()
896+
result = getAPropertySource()
827897
}
828898

829899
/**

javascript/ql/src/semmle/javascript/dataflow/Sources.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ class SourceNode extends DataFlow::Node {
155155
result.flowsTo(getAPropertyWrite(prop).getRhs())
156156
}
157157

158+
/**
159+
* Gets a source node whose value is stored in a property of this node.
160+
*/
161+
DataFlow::SourceNode getAPropertySource() {
162+
result.flowsTo(getAPropertyWrite().getRhs())
163+
}
164+
158165
/**
159166
* EXPERIMENTAL.
160167
*

javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,33 @@ private module CachedSteps {
9797
f = cap.getContainer()
9898
}
9999

100+
/**
101+
* Holds if the method invoked by `invoke` resolved to a member named `name` in `cls`
102+
* or one of its super classes.
103+
*/
104+
cached
105+
predicate callResolvesToMember(DataFlow::InvokeNode invoke, DataFlow::ClassNode cls, string name) {
106+
invoke = cls.getAnInstanceReference().getAMethodCall(name)
107+
or
108+
exists(DataFlow::ClassNode subclass |
109+
callResolvesToMember(invoke, subclass, name) and
110+
not exists(subclass.getAnInstanceMember(name)) and
111+
cls = subclass.getADirectSuperClass()
112+
)
113+
}
114+
100115
/**
101116
* Holds if `invk` may invoke `f`.
102117
*/
103118
cached
104-
predicate calls(DataFlow::InvokeNode invk, Function f) { f = invk.getACallee(0) }
119+
predicate calls(DataFlow::InvokeNode invk, Function f) {
120+
f = invk.getACallee(0)
121+
or
122+
exists(DataFlow::ClassNode cls, string name |
123+
callResolvesToMember(invk, cls, name) and
124+
f = cls.getInstanceMethod(name).getFunction()
125+
)
126+
}
105127

106128
/**
107129
* Holds if `invk` may invoke `f` indirectly through the given `callback` argument.

0 commit comments

Comments
 (0)