Merge pull request #1517 from asger-semmle/instance-type-tracking-final

Approved by xiemaisi
This commit is contained in:
semmle-qlci
2019-07-03 08:26:16 +01:00
committed by GitHub
4 changed files with 127 additions and 11 deletions

View File

@@ -67,14 +67,31 @@ class LocalObject extends DataFlow::SourceNode {
not exposedAsReceiver(this)
}
pragma[nomagic]
predicate hasOwnProperty(string name) {
// the property is defined in the initializer,
any(DataFlow::PropWrite write).writes(this, name, _) and
// and it is never deleted
not exists(DeleteExpr del, DataFlow::PropRef ref |
not hasDeleteWithName(name) and
// and there is no deleted property with computed name
not hasDeleteWithComputedProperty()
}
pragma[noinline]
private predicate hasDeleteWithName(string name) {
exists(DeleteExpr del, DataFlow::PropRef ref |
del.getOperand().flow() = ref and
flowsTo(ref.getBase()) and
(ref.getPropertyName() = name or not exists(ref.getPropertyName()))
ref.getPropertyName() = name
)
}
pragma[noinline]
private predicate hasDeleteWithComputedProperty() {
exists(DeleteExpr del, DataFlow::PropRef ref |
del.getOperand().flow() = ref and
flowsTo(ref.getBase()) and
not exists(ref.getPropertyName())
)
}
}

View File

@@ -642,16 +642,23 @@ class ClassNode extends DataFlow::SourceNode {
*/
FunctionNode getAStaticMethod() { result = impl.getAStaticMethod() }
/**
* Gets a dataflow node that refers to the superclass of this class.
*/
DataFlow::Node getASuperClassNode() { result = impl.getASuperClassNode() }
/**
* Gets a direct super class of this class.
*/
ClassNode getADirectSuperClass() {
result.getConstructor().getAstNode() = impl
.getASuperClassNode()
.analyze()
.getAValue()
.(AbstractCallable)
.getFunction()
result.getAClassReference().flowsTo(getASuperClassNode())
}
/**
* Gets a direct subclass of this class.
*/
final ClassNode getADirectSubClass() {
this = result.getADirectSuperClass()
}
/**
@@ -662,6 +669,63 @@ class ClassNode extends DataFlow::SourceNode {
or
result = getAnInstanceMember().getReceiver()
}
/**
* Gets the abstract value representing the class itself.
*/
AbstractValue getAbstractClassValue() {
result = this.(AnalyzedNode).getAValue()
}
/**
* Gets the abstract value representing an instance of this class.
*/
AbstractValue getAbstractInstanceValue() {
result = AbstractInstance::of(getAstNode())
}
/**
* Gets a dataflow node that refers to this class object.
*/
private DataFlow::SourceNode getAClassReference(DataFlow::TypeTracker t) {
t.start() and
result.(AnalyzedNode).getAValue() = getAbstractClassValue()
or
exists(DataFlow::TypeTracker t2 |
result = getAClassReference(t2).track(t2, t)
)
}
/**
* Gets a dataflow node that refers to this class object.
*/
DataFlow::SourceNode getAClassReference() {
result = getAClassReference(DataFlow::TypeTracker::end())
}
/**
* Gets a dataflow node that refers to an instance of this class.
*/
private DataFlow::SourceNode getAnInstanceReference(DataFlow::TypeTracker t) {
result = getAClassReference(t.continue()).getAnInstantiation()
or
t.start() and
result.(AnalyzedNode).getAValue() = getAbstractInstanceValue()
or
t.start() and
result = getAReceiverNode()
or
exists(DataFlow::TypeTracker t2 |
result = getAnInstanceReference(t2).track(t2, t)
)
}
/**
* Gets a dataflow node that refers to an instance of this class.
*/
DataFlow::SourceNode getAnInstanceReference() {
result = getAnInstanceReference(DataFlow::TypeTracker::end())
}
}
module ClassNode {
@@ -803,6 +867,9 @@ module ClassNode {
kind = MemberKind::method() and
result = getAPrototypeReference().getAPropertySource(name)
or
kind = MemberKind::method() and
result = getConstructor().getReceiver().getAPropertySource(name)
or
exists(PropertyAccessor accessor |
accessor = getAnAccessor(kind) and
accessor.getName() = name and
@@ -812,7 +879,10 @@ module ClassNode {
override FunctionNode getAnInstanceMember(MemberKind kind) {
kind = MemberKind::method() and
result = getAPrototypeReference().getAPropertyWrite().getRhs().getALocalSource()
result = getAPrototypeReference().getAPropertySource()
or
kind = MemberKind::method() and
result = getConstructor().getReceiver().getAPropertySource()
or
exists(PropertyAccessor accessor |
accessor = getAnAccessor(kind) and
@@ -823,7 +893,7 @@ module ClassNode {
override FunctionNode getStaticMethod(string name) { result = getAPropertySource(name) }
override FunctionNode getAStaticMethod() {
result = getAPropertyWrite().getRhs().getALocalSource()
result = getAPropertySource()
}
/**

View File

@@ -155,6 +155,13 @@ class SourceNode extends DataFlow::Node {
result.flowsTo(getAPropertyWrite(prop).getRhs())
}
/**
* Gets a source node whose value is stored in a property of this node.
*/
DataFlow::SourceNode getAPropertySource() {
result.flowsTo(getAPropertyWrite().getRhs())
}
/**
* EXPERIMENTAL.
*

View File

@@ -97,11 +97,33 @@ private module CachedSteps {
f = cap.getContainer()
}
/**
* Holds if the method invoked by `invoke` resolved to a member named `name` in `cls`
* or one of its super classes.
*/
cached
predicate callResolvesToMember(DataFlow::InvokeNode invoke, DataFlow::ClassNode cls, string name) {
invoke = cls.getAnInstanceReference().getAMethodCall(name)
or
exists(DataFlow::ClassNode subclass |
callResolvesToMember(invoke, subclass, name) and
not exists(subclass.getAnInstanceMember(name)) and
cls = subclass.getADirectSuperClass()
)
}
/**
* Holds if `invk` may invoke `f`.
*/
cached
predicate calls(DataFlow::InvokeNode invk, Function f) { f = invk.getACallee(0) }
predicate calls(DataFlow::InvokeNode invk, Function f) {
f = invk.getACallee(0)
or
exists(DataFlow::ClassNode cls, string name |
callResolvesToMember(invk, cls, name) and
f = cls.getInstanceMethod(name).getFunction()
)
}
/**
* Holds if `invk` may invoke `f` indirectly through the given `callback` argument.