mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
C#: Identify CIL methods that are stubs, and exclude them from dataflow.
This commit is contained in:
@@ -17,3 +17,4 @@ import Handler
|
||||
import ControlFlow
|
||||
import DataFlow
|
||||
import Attribute
|
||||
import Stubs
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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() + "\"" }
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
csharp/ql/src/semmle/code/cil/Stubs.qll
Normal file
43
csharp/ql/src/semmle/code/cil/Stubs.qll
Normal 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())
|
||||
}
|
||||
}
|
||||
@@ -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) |
|
||||
|
||||
2
csharp/ql/test/library-tests/cil/pdbs/Stubs.expected
Normal file
2
csharp/ql/test/library-tests/cil/pdbs/Stubs.expected
Normal 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 |
|
||||
5
csharp/ql/test/library-tests/cil/pdbs/Stubs.ql
Normal file
5
csharp/ql/test/library-tests/cil/pdbs/Stubs.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import cil::CIL
|
||||
|
||||
from Assembly asm
|
||||
where assemblyIsStub(asm)
|
||||
select asm
|
||||
Reference in New Issue
Block a user