C#: Identify CIL methods that are stubs, and exclude them from dataflow.

This commit is contained in:
calum
2019-02-15 15:29:15 +00:00
parent 60b121008f
commit 3da8d3e82d
8 changed files with 75 additions and 8 deletions

View File

@@ -17,3 +17,4 @@ import Handler
import ControlFlow
import DataFlow
import Attribute
import Stubs

View File

@@ -127,6 +127,9 @@ class FloatLiteral extends Literal, @cil_ldc_r { }
/** An expression that pushes a `null` value onto the stack. */
class NullLiteral extends Literal, @cil_ldnull { }
/** An expression that pushes a string onto the stack. */
class StringLiteral extends Literal, @cil_ldstr { }
/** A branch with one operand. */
class UnaryBranch extends ConditionalBranch, @cil_unary_jump {
override int getPopCount() { result = 1 }

View File

@@ -234,7 +234,7 @@ module Opcodes {
override string getOpcodeName() { result = "nop" }
}
class Ldstr extends Literal, @cil_ldstr {
class Ldstr extends StringLiteral, @cil_ldstr {
override string getOpcodeName() { result = "ldstr" }
override string getExtra() { result = "\"" + getValue() + "\"" }

View File

@@ -50,6 +50,11 @@ class MethodImplementation extends EntryPoint, @cil_method_implementation {
int getStackSize() { cil_method_stack_size(this, result) }
override string toString() { result = getMethod().toString() }
/** Gets a string representing the disassembly of this implementation. */
string getDisassembly() {
result = concat(Instruction i | i = this.getAnInstruction() | i.toString(), ", " order by i.getIndex())
}
}
/**
@@ -63,6 +68,11 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
*/
MethodImplementation getAnImplementation() { result.getMethod() = this }
/** Gets the "best" implementation of a method, if any. */
BestImplementation getImplementation() {
result = getAnImplementation()
}
override Method getMethod() { result = this }
override string getName() { cil_method(this, result, _, _) }
@@ -102,7 +112,7 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
int getCallPopCount() { result = count(getRawParameter(_)) }
/** Gets a method called by this method. */
Method getACallee() { result = getAnImplementation().getAnInstruction().(Call).getTarget() }
Method getACallee() { result = getImplementation().getAnInstruction().(Call).getTarget() }
/** Holds if this method is `virtual`. */
predicate isVirtual() { cil_virtual(this) }
@@ -168,11 +178,14 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
/** Gets a method that overrides this method, if any. */
final Method getAnOverrider() { result.getOverriddenMethod() = this }
override predicate hasBody() { exists(getAnImplementation()) }
override predicate hasBody() { exists(getImplementation()) }
override predicate canReturn(DotNet::Expr expr) {
exists(Return ret | ret.getImplementation().getMethod() = this and expr = ret.getExpr())
}
/** Gets a string representing the disassembly of this method. There can be multiple implementations. */
string getADisassembly() { result = this.getAnImplementation().getDisassembly() }
}
/** A destructor/finalizer. */
@@ -198,7 +211,7 @@ class InstanceConstructor extends Constructor {
/** A method that always returns the `this` parameter. */
class ChainingMethod extends Method {
ChainingMethod() {
forex(Return ret | ret = getAnImplementation().getAnInstruction() |
forex(Return ret | ret = getImplementation().getAnInstruction() |
ret.getExpr() instanceof ThisAccess
)
}
@@ -232,7 +245,7 @@ class TrivialGetter extends Method {
/** Gets the underlying field of this getter. */
Field getField() {
getAnImplementation().getAnInstruction().(FieldReadAccess).getTarget() = result
getImplementation().getAnInstruction().(FieldReadAccess).getTarget() = result
}
}
@@ -249,7 +262,7 @@ class Setter extends Accessor {
*/
class TrivialSetter extends Method {
TrivialSetter() {
exists(MethodImplementation impl | impl = getAnImplementation() |
exists(MethodImplementation impl | impl = getImplementation() |
impl.getInstruction(0) instanceof ThisAccess and
impl.getInstruction(1).(ParameterReadAccess).getTarget().getIndex() = 1 and
impl.getInstruction(2) instanceof FieldWriteAccess
@@ -258,7 +271,7 @@ class TrivialSetter extends Method {
/** Gets the underlying field of this setter. */
Field getField() {
result = getAnImplementation().getAnInstruction().(FieldWriteAccess).getTarget()
result = getImplementation().getAnInstruction().(FieldWriteAccess).getTarget()
}
}

View File

@@ -0,0 +1,43 @@
/**
* Provides classes and predicates relating to identifying stub code.
*/
import CIL
/**
* The average number of instructions per method,
* below which an assembly is probably a stub.
*/
private float stubInstructionThreshold() { result = 5.1 }
/**
* A simple heuristic for determining whether an assembly is a
* reference assembly where the method bodies have dummy implementations.
* Look at the average number of instructions per method.
*/
cached
predicate assemblyIsStub(Assembly asm) {
exists(int totalInstructions, int totalImplementations |
totalInstructions = count(Instruction i | i.getImplementation().getLocation() = asm) and
totalImplementations = count(MethodImplementation i | i.getImplementation().getLocation() = asm) and
totalInstructions.(float) / totalImplementations.(float) < stubInstructionThreshold()
)
}
/**
* A method implementation that is the "best" one for a particular method,
* if there are several potential implementations to choose between, and
* excludes implementations that are probably from stub/reference assemblies.
*/
class BestImplementation extends MethodImplementation {
BestImplementation() {
not assemblyIsStub(this.getLocation()) and
not exists(MethodImplementation better | this.getMethod() = better.getMethod() |
this.getNumberOfInstructions() > better.getNumberOfInstructions()
or
this.getNumberOfInstructions() = better.getNumberOfInstructions() and
this.getLocation().getFile().toString() < better.getLocation().getFile().toString()
) and
exists(this.getAnInstruction())
}
}

View File

@@ -1436,7 +1436,7 @@ module DataFlow {
TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof Expr } or
TSsaDefinitionNode(Ssa::Definition def) or
TCilParameterNode(CIL::Parameter p) { p.getMethod().hasBody() } or
TCilExprNode(CIL::Expr e) or
TCilExprNode(CIL::Expr e) { e.getImplementation() instanceof CIL::BestImplementation } or
TImplicitDelegateCallNode(DelegateArgumentToLibraryCallable arg) or
TImplicitCapturedArgumentNode(Call c, LocalScopeVariable v) {
exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowIn(_, c) |

View File

@@ -0,0 +1,2 @@
| EmbeddedPdb.dll:0:0:0:0 | EmbeddedPdb, Version=1.0.0.0, Culture=neutral |
| PdbOnly.dll:0:0:0:0 | PdbOnly, Version=1.0.0.0, Culture=neutral |

View File

@@ -0,0 +1,5 @@
import cil::CIL
from Assembly asm
where assemblyIsStub(asm)
select asm