Files
codeql/csharp/ql/lib/semmle/code/cil/InstructionGroups.qll
2021-12-09 15:51:54 +01:00

264 lines
8.2 KiB
Plaintext

/**
* Provides classes representing various classes of expression
* and other instructions.
*/
private import CIL
private import dotnet
/**
* An instruction that pushes a value onto the stack.
*/
class Expr extends DotNet::Expr, Instruction, @cil_expr {
override int getPushCount() { result = 1 }
override Type getType() { result = Instruction.super.getType() }
override Method getEnclosingCallable() { result = this.getImplementation().getMethod() }
/**
* The "parent" of a CIL expression is taken to be the instruction
* that consumes the value pushed by this instruction.
*/
override Expr getParent() { this = result.getAnOperand() }
}
/** An instruction that changes control flow. */
class Branch extends Instruction, @cil_jump {
/** Gets the instruction that is jumped to. */
Instruction getTarget() { cil_jump(this, result) }
override string getExtra() { result = this.getTarget().getIndex() + ":" }
}
/** An instruction that unconditionally jumps to another instruction. */
class UnconditionalBranch extends Branch, @cil_unconditional_jump {
override Instruction getASuccessorType(FlowType t) {
t instanceof NormalFlow and result = this.getTarget()
}
override predicate canFlowNext() { none() }
}
/** An instruction that jumps to a target based on a condition. */
class ConditionalBranch extends Branch, @cil_conditional_jump {
override Instruction getASuccessorType(FlowType t) {
t instanceof TrueFlow and result = this.getTarget()
or
t instanceof FalseFlow and result = this.getImplementation().getInstruction(this.getIndex() + 1)
}
override int getPushCount() { result = 0 }
}
/** An expression with two operands. */
class BinaryExpr extends Expr, @cil_binary_expr {
override int getPopCount() { result = 2 }
}
/** An expression with one operand. */
class UnaryExpr extends Expr, @cil_unary_expr {
override int getPopCount() { result = 1 }
/** Gets the operand of this unary expression. */
Expr getOperand() { result = this.getOperand(0) }
}
/** A binary expression that compares two values. */
class ComparisonOperation extends BinaryExpr, @cil_comparison_operation {
override BoolType getType() { exists(result) }
}
/** A binary arithmetic expression. */
class BinaryArithmeticExpr extends BinaryExpr, @cil_binary_arithmetic_operation {
override Type getType() {
exists(Type t0, Type t1 |
t0 = this.getOperandType(0).getUnderlyingType() and
t1 = this.getOperandType(1).getUnderlyingType()
|
t0 = t1 and result = t0
or
t0.getConversionIndex() < t1.getConversionIndex() and result = t1
or
t0.getConversionIndex() > t1.getConversionIndex() and result = t0
)
}
}
/** A binary bitwise expression. */
class BinaryBitwiseOperation extends BinaryExpr, @cil_binary_bitwise_operation {
// This is wrong but efficient - should depend on the types of the operands.
override IntType getType() { exists(result) }
}
/** A unary bitwise expression. */
class UnaryBitwiseOperation extends UnaryExpr, @cil_unary_bitwise_operation {
// This is wrong but efficient - should depend on the types of the operands.
override IntType getType() { exists(result) }
}
/** A unary expression that converts a value from one primitive type to another. */
class Conversion extends UnaryExpr, @cil_conversion_operation {
/** Gets the expression being converted. */
Expr getExpr() { result = this.getOperand(0) }
}
/** A branch that leaves the scope of a `Handler`. */
class Leave extends UnconditionalBranch, @cil_leave_any { }
/** An expression that pushes a literal value onto the stack. */
class Literal extends DotNet::Literal, Expr, @cil_literal {
/** Gets the pushed value. */
override string getValue() { cil_value(this, result) }
override string getExtra() { result = this.getValue() }
}
/** An integer literal. */
class IntLiteral extends Literal, @cil_ldc_i {
override string getExtra() { none() }
override IntType getType() { exists(result) }
}
/** An expression that pushes a `float`/`Single`. */
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 }
override int getPushCount() { result = 0 }
}
/** A branch with two operands. */
class BinaryBranch extends ConditionalBranch, @cil_binary_jump {
override int getPopCount() { result = 2 }
override int getPushCount() { result = 0 }
}
/** A call. */
class Call extends Expr, DotNet::Call, @cil_call_any {
/** Gets the method that is called. */
override Method getTarget() { cil_access(this, result) }
override Method getARuntimeTarget() { result = this.getTarget().getAnOverrider*() }
override string getExtra() { result = this.getTarget().getQualifiedName() }
/**
* Gets the return type of the call. Methods that do not return a value
* return the `void` type, `System.Void`, although the value of `getPushCount` is
* 0 in this case.
*/
override Type getType() { result = this.getTarget().getReturnType() }
// The number of items popped/pushed from the stack
// depends on the target of the call.
override int getPopCount() { result = this.getTarget().getCallPopCount() }
override int getPushCount() { result = this.getTarget().getCallPushCount() }
/**
* Holds if this is a "tail call", meaning that control does not return to the
* calling method.
*/
predicate isTailCall() {
this.getImplementation().getInstruction(this.getIndex() - 1) instanceof Opcodes::Tail
}
/** Holds if this call is virtual and could go to an overriding method. */
predicate isVirtual() { none() }
override Expr getRawArgument(int i) { result = this.getOperand(this.getPopCount() - i - 1) }
/** Gets the qualifier of this call, if any. */
Expr getQualifier() { result = this.getRawArgument(0) and not this.getTarget().isStatic() }
override Expr getArgument(int i) {
if this.getTarget().isStatic()
then result = this.getRawArgument(i)
else (
result = this.getRawArgument(i + 1) and i >= 0
)
}
override Expr getArgumentForParameter(DotNet::Parameter param) {
exists(int index |
result = this.getRawArgument(index) and param = this.getTarget().getRawParameter(index)
)
}
}
/** A tail call. */
class TailCall extends Call {
TailCall() { this.isTailCall() }
override predicate canFlowNext() { none() }
}
/** A call to a static target. */
class StaticCall extends Call {
StaticCall() { not this.isVirtual() }
}
/** A call to a virtual target. */
class VirtualCall extends Call {
VirtualCall() { this.isVirtual() }
}
/** A read of an array element. */
class ReadArrayElement extends BinaryExpr, @cil_read_array {
/** Gets the array being read. */
Expr getArray() { result = this.getOperand(1) }
/** Gets the index into the array. */
Expr getArrayIndex() { result = this.getOperand(0) }
}
/** A write of an array element. */
class WriteArrayElement extends Instruction, @cil_write_array {
override int getPushCount() { result = 0 }
override int getPopCount() { result = 3 }
}
/** A `return` statement. */
class Return extends Instruction, @cil_ret {
/** Gets the expression being returned, if any. */
Expr getExpr() { result = this.getOperand(0) }
override predicate canFlowNext() { none() }
}
/** A `throw` statement. */
class Throw extends Instruction, DotNet::Throw, @cil_throw_any {
override Expr getExpr() { result = this.getOperand(0) }
/** Gets the type of the exception being thrown. */
Type getExceptionType() { result = this.getOperandType(0) }
override predicate canFlowNext() { none() }
}
/** Stores a value at an address/location. */
class StoreIndirect extends Instruction, @cil_stind {
override int getPopCount() { result = 2 }
/** Gets the location to store the value at. */
Expr getAddress() { result = this.getOperand(1) }
/** Gets the value to store. */
Expr getExpr() { result = this.getOperand(0) }
}
/** Loads a value from an address/location. */
class LoadIndirect extends UnaryExpr, @cil_ldind { }