mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
Merge pull request #412 from adityasharad/merge/master-next-061118
Merge master into next.
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
## General improvements
|
||||
|
||||
* Control flow graph improvements:
|
||||
* The control flow graph construction now takes simple Boolean conditions on local scope variables into account. For example, in `if (b) x = 0; if (b) x = 1;`, the control flow graph will reflect that taking the `true` (resp. `false`) branch in the first condition implies taking the same branch in the second condition. In effect, the first assignment to `x` will now be identified as being dead.
|
||||
* The control flow graph construction now takes simple Boolean conditions on local scope variables into account. For example, in `if (b) x = 0; if (b) x = 1;`, the control flow graph will reflect that taking the `true` (resp. `false`) branch in the first condition implies taking the same branch in the second condition. In effect, the first assignment to `x` will now be identified as being dead.
|
||||
* Code that is only reachable from a constant failing assertion, such as `Debug.Assert(false)`, is considered to be unreachable.
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
| Inconsistent lock sequence (`cs/inconsistent-lock-sequence`) | More results | This query now finds inconsistent lock sequences globally across calls. |
|
||||
| Local scope variable shadows member (`cs/local-shadows-member`) | Fewer results | Results have been removed where a constructor parameter shadows a member, because the parameter is probably used to initialize the member. |
|
||||
| Cross-site scripting (`cs/web/xss`) | More results | This query now finds cross-site scripting vulnerabilities in ASP.NET Core applications. |
|
||||
| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* |
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Arbitrary file write during archive extraction ("Zip Slip") (`java/zipslip`) | security, external/cwe/cwe-022 | Identifies extraction routines that allow arbitrary file overwrite vulnerabilities. |
|
||||
| Missing catch of NumberFormatException (`java/uncaught-number-format-exception`) | reliability, external/cwe/cwe-248 | Finds calls to `Integer.parseInt` and similar string-to-number conversions that might raise a `NumberFormatException` without a corresponding `catch`-clause. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following features:
|
||||
- file system access, for example through [fs-extra](https://github.com/jprichardson/node-fs-extra) or [globby](https://www.npmjs.com/package/globby)
|
||||
- outbound network access, for example through the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
|
||||
- the [Google Cloud Spanner](https://cloud.google.com/spanner) database
|
||||
- the [Google Cloud Spanner](https://cloud.google.com/spanner), [lodash](https://lodash.com) and [underscore](https://underscorejs.org/) libraries
|
||||
|
||||
* The type inference now handles nested imports (that is, imports not appearing at the toplevel). This may yield fewer false-positive results on projects that use this non-standard language feature.
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
| Remote property injection | Fewer results | The precision of this rule has been revised to "medium". Results are no longer shown on LGTM by default. |
|
||||
| Missing CSRF middleware | Fewer false-positive results | This rule now recognizes additional CSRF protection middlewares. |
|
||||
| Server-side URL redirect | More results | This rule now recognizes redirection calls in more cases. |
|
||||
| Unused variable, import, function or class | Fewer false-positive results | This rule now flags fewer variables that may be used by `eval` calls. |
|
||||
| Unused variable, import, function or class | Fewer results | This rule now flags import statements with multiple unused imports once. |
|
||||
| User-controlled bypass of security check | Fewer results | This rule no longer flags conditions that guard early returns. The precision of this rule has been revised to "medium". Results are no longer shown on LGTM by default. |
|
||||
| Whitespace contradicts operator precedence | Fewer false-positive results | This rule no longer flags operators with asymmetric whitespace. |
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/FunctionIR.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/FunctionIR.qll"
|
||||
],
|
||||
"C++ IR OperandTag": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/OperandTag.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/OperandTag.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/OperandTag.qll"
|
||||
"C++ IR Operand": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll"
|
||||
],
|
||||
"C++ IR IRImpl": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
|
||||
@@ -16,7 +16,7 @@ class MemoryAccessKind extends TMemoryAccessKind {
|
||||
|
||||
/**
|
||||
* The operand or result accesses memory at the address specified by the
|
||||
* `LoadStoreAddressOperand` on the same instruction.
|
||||
* `AddressOperand` on the same instruction.
|
||||
*/
|
||||
class IndirectMemoryAccess extends MemoryAccessKind, TIndirectMemoryAccess {
|
||||
override string toString() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import FunctionIR
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import OperandTag
|
||||
import Operand
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ private import internal.IRInternal
|
||||
import FunctionIR
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import OperandTag
|
||||
import Operand
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
|
||||
class InstructionTag = Construction::InstructionTagType;
|
||||
|
||||
@@ -22,21 +23,21 @@ module InstructionSanity {
|
||||
exists(Opcode opcode |
|
||||
opcode = instr.getOpcode() and
|
||||
(
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperand or
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperandTag or
|
||||
(
|
||||
opcode instanceof BinaryOpcode and
|
||||
(
|
||||
tag instanceof LeftOperand or
|
||||
tag instanceof RightOperand
|
||||
tag instanceof LeftOperandTag or
|
||||
tag instanceof RightOperandTag
|
||||
)
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperand or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof LoadStoreAddressOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperand or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperand or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperand or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperand or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperand
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -45,26 +46,34 @@ module InstructionSanity {
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, OperandTag tag) {
|
||||
expectsOperand(instr, tag) and not exists(instr.getOperand(tag))
|
||||
expectsOperand(instr, tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperand) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperand) and
|
||||
not (instr instanceof PhiInstruction and tag instanceof PhiOperand)
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(Instruction instr, OperandTag tag) {
|
||||
strictcount(instr.getOperand(tag)) > 1 and
|
||||
not tag instanceof UnmodeledUseOperand
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +83,7 @@ module InstructionSanity {
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiOperand operand |
|
||||
exists(instr.getOperand(operand)) and
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
@@ -98,14 +107,13 @@ module InstructionSanity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `op` consumes an operand `operand` that was defined in
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Instruction op, Instruction operand, OperandTag tag
|
||||
) {
|
||||
operand = op.getOperand(tag) and
|
||||
operand.getFunctionIR() != op.getFunctionIR()
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getInstruction() = instr and
|
||||
operand.getDefinitionInstruction() = defInstr and
|
||||
instr.getFunctionIR() != defInstr.getFunctionIR()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,20 +254,6 @@ class Instruction extends Construction::TInstruction {
|
||||
result = getResultId() + "(" + getResultTypeString() + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the specified operand, suitable for display in IR
|
||||
* dumps. This consists of the result ID of the instruction consumed by the
|
||||
* operand, plus a label identifying the operand kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
string getOperandString(OperandTag tag) {
|
||||
exists(Instruction operand |
|
||||
operand = getOperand(tag) and
|
||||
result = tag.getLabel() + operand.getResultId()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operands of this instruction, suitable for
|
||||
* display in IR dumps.
|
||||
@@ -267,9 +261,9 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `func:r3_4, this:r3_5`
|
||||
*/
|
||||
string getOperandsString() {
|
||||
result = concat(OperandTag tag, Instruction operand |
|
||||
operand = getOperand(tag) |
|
||||
tag.getLabel() + operand.getResultId(), ", " order by tag.getSortOrder()
|
||||
result = concat(Operand operand |
|
||||
operand = getAnOperand() |
|
||||
operand.getDumpString(), ", " order by operand.getDumpSortOrder()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -333,7 +327,6 @@ class Instruction extends Construction::TInstruction {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -397,35 +390,19 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that produced the value of the specified source
|
||||
* operand.
|
||||
* Gets all direct uses of the result of this instruction.
|
||||
*/
|
||||
final Instruction getOperand(OperandTag tag) {
|
||||
result = Construction::getInstructionOperand(this, tag)
|
||||
final Operand getAUse() {
|
||||
result.getDefinitionInstruction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all instructions consumed by this instruction's operands.
|
||||
* Gets all of this instruction's operands.
|
||||
*/
|
||||
final Instruction getAnOperand() {
|
||||
result = getOperand(_)
|
||||
final Operand getAnOperand() {
|
||||
result.getInstruction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this instruction has a memory operand with the specified tag.
|
||||
*/
|
||||
final predicate isMemoryOperand(OperandTag tag) {
|
||||
exists(getOperandMemoryAccess(tag))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the specified operand. Holds
|
||||
* only for memory operands.
|
||||
*/
|
||||
MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this instruction produces a memory result.
|
||||
*/
|
||||
@@ -461,9 +438,7 @@ class Instruction extends Construction::TInstruction {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not exists(Instruction useInstr, UnmodeledUseOperand useTag |
|
||||
this = useInstr.getOperand(useTag)
|
||||
)
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +577,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
|
||||
final Instruction getObjectAddress() {
|
||||
result = getOperand(unaryOperand())
|
||||
result = getAnOperand().(UnaryOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,12 +615,7 @@ class ReturnValueInstruction extends ReturnInstruction {
|
||||
}
|
||||
|
||||
final Instruction getReturnValue() {
|
||||
result = getOperand(returnValueOperand())
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(ReturnValueOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
result = getAnOperand().(ReturnValueOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +625,7 @@ class CopyInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getSourceValue() {
|
||||
result = getOperand(copySourceOperand())
|
||||
result = getAnOperand().(CopySourceOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,13 +640,8 @@ class LoadInstruction extends CopyInstruction {
|
||||
opcode instanceof Opcode::Load
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(CopySourceOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
final Instruction getSourceAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +655,7 @@ class StoreInstruction extends CopyInstruction {
|
||||
}
|
||||
|
||||
final Instruction getDestinationAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,7 +665,7 @@ class ConditionalBranchInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getCondition() {
|
||||
result = getOperand(conditionOperand())
|
||||
result = getAnOperand().(ConditionOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getTrueSuccessor() {
|
||||
@@ -758,11 +723,11 @@ class BinaryInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getLeftOperand() {
|
||||
result = getOperand(leftOperand())
|
||||
result = getAnOperand().(LeftOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getRightOperand() {
|
||||
result = getOperand(rightOperand())
|
||||
result = getAnOperand().(RightOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,7 +838,7 @@ class UnaryInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getOperand() {
|
||||
result = getOperand(unaryOperand())
|
||||
result = getAnOperand().(UnaryOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,6 +957,7 @@ class RelationalInstruction extends CompareInstruction {
|
||||
RelationalInstruction() {
|
||||
opcode instanceof RelationalOpcode
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand on the "greater" (or "greater-or-equal") side
|
||||
* of this relational instruction, that is, the side that is larger
|
||||
@@ -1011,6 +977,7 @@ class RelationalInstruction extends CompareInstruction {
|
||||
Instruction getLesserOperand() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this relational instruction is strict (is not an "or-equal" instruction).
|
||||
*/
|
||||
@@ -1097,7 +1064,7 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getExpression() {
|
||||
result = getOperand(conditionOperand())
|
||||
result = getAnOperand().(ConditionOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getACaseSuccessor() {
|
||||
@@ -1117,7 +1084,7 @@ class CallInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getCallTarget() {
|
||||
result = getOperand(callTargetOperand())
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,24 +1105,18 @@ class ThrowValueInstruction extends ThrowInstruction {
|
||||
opcode instanceof Opcode::ThrowValue
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(ExceptionOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the address of the exception thrown by this instruction.
|
||||
*/
|
||||
final Instruction getExceptionAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception thrown by this instruction.
|
||||
*/
|
||||
final Instruction getException() {
|
||||
result = getOperand(exceptionOperand())
|
||||
result = getAnOperand().(ExceptionOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,11 +1197,6 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
override string getOperandsString() {
|
||||
result = "mu*"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(UnmodeledUseOperand))) and
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class PhiInstruction extends Instruction {
|
||||
@@ -1248,11 +1204,6 @@ class PhiInstruction extends Instruction {
|
||||
opcode instanceof Opcode::Phi
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(PhiOperand))) and
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
|
||||
private newtype TOperand =
|
||||
TNonPhiOperand(Instruction instr, OperandTag tag, Instruction defInstr) {
|
||||
defInstr = Construction::getInstructionOperandDefinition(instr, tag)
|
||||
} or
|
||||
TPhiOperand(PhiInstruction instr, Instruction defInstr, IRBlock predecessorBlock) {
|
||||
defInstr = Construction::getPhiInstructionOperandDefinition(instr, predecessorBlock)
|
||||
}
|
||||
|
||||
/**
|
||||
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
|
||||
*/
|
||||
class Operand extends TOperand {
|
||||
string toString() {
|
||||
result = "Operand"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
Instruction getInstruction() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` whose result is the value of the operand.
|
||||
*/
|
||||
Instruction getDefinitionInstruction() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a prefix to use when dumping the operand in an operand list.
|
||||
*/
|
||||
string getDumpLabel() {
|
||||
result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
|
||||
* result ID of the instruction consumed by the operand, plus a label identifying the operand
|
||||
* kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
final string getDumpString() {
|
||||
result = getDumpLabel() + getDefinitionInstruction().getResultId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order in which the operand should be sorted in the operand list.
|
||||
*/
|
||||
int getDumpSortOrder() {
|
||||
result = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand. Holds only for memory operands.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
* value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2`
|
||||
* is `r1`.
|
||||
*/
|
||||
final AddressOperand getAddressOperand() {
|
||||
getMemoryAccess() instanceof IndirectMemoryAccess and
|
||||
result.getInstruction() = getInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that is not an operand of a `PhiInstruction`.
|
||||
*/
|
||||
class NonPhiOperand extends Operand, TNonPhiOperand {
|
||||
Instruction instr;
|
||||
Instruction defInstr;
|
||||
OperandTag tag;
|
||||
|
||||
NonPhiOperand() {
|
||||
this = TNonPhiOperand(instr, tag, defInstr)
|
||||
}
|
||||
|
||||
override final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override final Instruction getDefinitionInstruction() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = tag.getLabel()
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = tag.getSortOrder()
|
||||
}
|
||||
|
||||
final OperandTag getOperandTag() {
|
||||
result = tag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class AddressOperand extends NonPhiOperand {
|
||||
AddressOperand() {
|
||||
this = TNonPhiOperand(_, addressOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Address"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that copies this value to its
|
||||
* result (e.g. `Copy`, `Load`, `Store`).
|
||||
*/
|
||||
class CopySourceOperand extends NonPhiOperand {
|
||||
CopySourceOperand() {
|
||||
this = TNonPhiOperand(_, copySourceOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "CopySource"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
instr.getOpcode() instanceof Opcode::Load and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`).
|
||||
*/
|
||||
class UnaryOperand extends NonPhiOperand {
|
||||
UnaryOperand() {
|
||||
this = TNonPhiOperand(_, unaryOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends NonPhiOperand {
|
||||
LeftOperand() {
|
||||
this = TNonPhiOperand(_, leftOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends NonPhiOperand {
|
||||
RightOperand() {
|
||||
this = TNonPhiOperand(_, rightOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value operand of a `ReturnValue` instruction.
|
||||
*/
|
||||
class ReturnValueOperand extends NonPhiOperand {
|
||||
ReturnValueOperand() {
|
||||
this = TNonPhiOperand(_, returnValueOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ReturnValue"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class ExceptionOperand extends NonPhiOperand {
|
||||
ExceptionOperand() {
|
||||
this = TNonPhiOperand(_, exceptionOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Exception"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends NonPhiOperand {
|
||||
ConditionOperand() {
|
||||
this = TNonPhiOperand(_, conditionOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperand extends NonPhiOperand {
|
||||
UnmodeledUseOperand() {
|
||||
this = TNonPhiOperand(_, unmodeledUseOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends NonPhiOperand {
|
||||
CallTargetOperand() {
|
||||
this = TNonPhiOperand(_, callTargetOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
class ArgumentOperand extends NonPhiOperand {
|
||||
ArgumentOperand() {
|
||||
exists(ArgumentOperandTag argTag |
|
||||
this = TNonPhiOperand(_, argTag, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand {
|
||||
ThisArgumentOperand() {
|
||||
this = TNonPhiOperand(_, thisArgumentOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ThisArgument"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand {
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperand() {
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
this = TNonPhiOperand(_, argTag, _) and
|
||||
argIndex = argTag.getArgIndex()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiOperand extends Operand, TPhiOperand {
|
||||
PhiInstruction instr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
|
||||
PhiOperand() {
|
||||
this = TPhiOperand(instr, defInstr, predecessorBlock)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Phi"
|
||||
}
|
||||
|
||||
override final PhiInstruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override final Instruction getDefinitionInstruction() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = 11 + getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the predecessor block from which this value comes.
|
||||
*/
|
||||
final IRBlock getPredecessorBlock() {
|
||||
result = predecessorBlock
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that reads a value from memory.
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
MemoryOperand() {
|
||||
exists(getMemoryAccess())
|
||||
}
|
||||
}
|
||||
@@ -46,18 +46,20 @@ private IntValue getFieldBitOffset(Field field) {
|
||||
* not result in any address held in that operand from escaping beyond the
|
||||
* instruction.
|
||||
*/
|
||||
predicate operandIsConsumedWithoutEscaping(Instruction instr, OperandTag tag) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
(
|
||||
// The source/destination address of a Load/Store does not escape (but the
|
||||
// loaded/stored value could).
|
||||
tag instanceof LoadStoreAddressOperand or
|
||||
// Neither operand of a Compare escapes.
|
||||
instr instanceof CompareInstruction or
|
||||
// Neither operand of a PointerDiff escapes.
|
||||
instr instanceof PointerDiffInstruction or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultType() instanceof BoolType
|
||||
predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
||||
// The source/destination address of a Load/Store does not escape (but the
|
||||
// loaded/stored value could).
|
||||
operand instanceof AddressOperand or
|
||||
exists (Instruction instr |
|
||||
instr = operand.getInstruction() and
|
||||
(
|
||||
// Neither operand of a Compare escapes.
|
||||
instr instanceof CompareInstruction or
|
||||
// Neither operand of a PointerDiff escapes.
|
||||
instr instanceof PointerDiffInstruction or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultType() instanceof BoolType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,43 +98,44 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
|
||||
* `bitOffset`. If the address is propagated, but the offset is not known to be
|
||||
* a constant, then `bitOffset` is unknown.
|
||||
*/
|
||||
predicate operandIsPropagated(Instruction instr, OperandTag tag,
|
||||
IntValue bitOffset) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
(
|
||||
// Converting to a non-virtual base class adds the offset of the base class.
|
||||
exists(ConvertToBaseInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||
) or
|
||||
// Converting to a derived class subtracts the offset of the base class.
|
||||
exists(ConvertToDerivedInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
|
||||
) or
|
||||
// Converting to a virtual base class adds an unknown offset.
|
||||
predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
exists(Instruction instr |
|
||||
instr = operand.getInstruction() and
|
||||
(
|
||||
instr instanceof ConvertToVirtualBaseInstruction and
|
||||
bitOffset = Ints::unknown()
|
||||
) or
|
||||
// Conversion to another pointer type propagates the source address.
|
||||
exists(ConvertInstruction convert, Type resultType |
|
||||
convert = instr and
|
||||
resultType = convert.getResultType() and
|
||||
// Converting to a non-virtual base class adds the offset of the base class.
|
||||
exists(ConvertToBaseInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||
) or
|
||||
// Converting to a derived class subtracts the offset of the base class.
|
||||
exists(ConvertToDerivedInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
|
||||
) or
|
||||
// Converting to a virtual base class adds an unknown offset.
|
||||
(
|
||||
resultType instanceof PointerType or
|
||||
resultType instanceof Class //REVIEW: Remove when all glvalues are pointers
|
||||
) and
|
||||
bitOffset = 0
|
||||
) or
|
||||
// Adding an integer to or subtracting an integer from a pointer propagates
|
||||
// the address with an offset.
|
||||
bitOffset = getPointerBitOffset(instr.(PointerOffsetInstruction)) or
|
||||
// Computing a field address from a pointer propagates the address plus the
|
||||
// offset of the field.
|
||||
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or
|
||||
// A copy propagates the source value.
|
||||
tag instanceof CopySourceOperand and bitOffset = 0
|
||||
instr instanceof ConvertToVirtualBaseInstruction and
|
||||
bitOffset = Ints::unknown()
|
||||
) or
|
||||
// Conversion to another pointer type propagates the source address.
|
||||
exists(ConvertInstruction convert, Type resultType |
|
||||
convert = instr and
|
||||
resultType = convert.getResultType() and
|
||||
(
|
||||
resultType instanceof PointerType or
|
||||
resultType instanceof Class //REVIEW: Remove when all glvalues are pointers
|
||||
) and
|
||||
bitOffset = 0
|
||||
) or
|
||||
// Adding an integer to or subtracting an integer from a pointer propagates
|
||||
// the address with an offset.
|
||||
bitOffset = getPointerBitOffset(instr.(PointerOffsetInstruction)) or
|
||||
// Computing a field address from a pointer propagates the address plus the
|
||||
// offset of the field.
|
||||
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or
|
||||
// A copy propagates the source value.
|
||||
operand instanceof CopySourceOperand and bitOffset = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,16 +143,15 @@ predicate operandIsPropagated(Instruction instr, OperandTag tag,
|
||||
* Holds if any address held in operand number `tag` of instruction `instr`
|
||||
* escapes outside the domain of the analysis.
|
||||
*/
|
||||
predicate operandEscapes(Instruction instr, OperandTag tag) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
predicate operandEscapes(Operand operand) {
|
||||
// Conservatively assume that the address escapes unless one of the following
|
||||
// holds:
|
||||
not (
|
||||
// The operand is used in a way that does not escape the instruction
|
||||
operandIsConsumedWithoutEscaping(instr, tag) or
|
||||
operandIsConsumedWithoutEscaping(operand) or
|
||||
// The address is propagated to the result of the instruction, but that
|
||||
// result does not itself escape.
|
||||
operandIsPropagated(instr, tag, _) and not resultEscapes(instr)
|
||||
operandIsPropagated(operand, _) and not resultEscapes(operand.getInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -159,10 +161,7 @@ predicate operandEscapes(Instruction instr, OperandTag tag) {
|
||||
*/
|
||||
predicate resultEscapes(Instruction instr) {
|
||||
// The result escapes if it has at least one use that escapes.
|
||||
exists(Instruction useInstr, OperandTag useOperandTag |
|
||||
useInstr.getOperand(useOperandTag) = instr and
|
||||
operandEscapes(useInstr, useOperandTag)
|
||||
)
|
||||
operandEscapes(instr.getAUse())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,12 +202,12 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset)
|
||||
instr.(VariableAddressInstruction).getVariable() = var and
|
||||
bitOffset = 0
|
||||
) or
|
||||
exists(OperandTag operandTag, IntValue originalBitOffset,
|
||||
IntValue propagatedBitOffset |
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
operand = instr.getAnOperand() and
|
||||
// If an operand is propagated, then the result points to the same variable,
|
||||
// offset by the bit offset from the propagation.
|
||||
resultPointsTo(instr.getOperand(operandTag), var, originalBitOffset) and
|
||||
operandIsPropagated(instr, operandTag, propagatedBitOffset) and
|
||||
resultPointsTo(operand.getDefinitionInstruction(), var, originalBitOffset) and
|
||||
operandIsPropagated(operand, propagatedBitOffset) and
|
||||
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,30 +1,13 @@
|
||||
import SSAConstructionInternal
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
import NewIR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import NewIR
|
||||
import IRBlockConstruction as BlockConstruction
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
|
||||
private OldIR::OperandTag getOldOperandTag(OperandTag newTag) {
|
||||
newTag instanceof LoadStoreAddressOperand and result instanceof OldIR::LoadStoreAddressOperand or
|
||||
newTag instanceof CopySourceOperand and result instanceof OldIR::CopySourceOperand or
|
||||
newTag instanceof UnaryOperand and result instanceof OldIR::UnaryOperand or
|
||||
newTag instanceof LeftOperand and result instanceof OldIR::LeftOperand or
|
||||
newTag instanceof RightOperand and result instanceof OldIR::RightOperand or
|
||||
newTag instanceof ReturnValueOperand and result instanceof OldIR::ReturnValueOperand or
|
||||
newTag instanceof ExceptionOperand and result instanceof OldIR::ExceptionOperand or
|
||||
newTag instanceof ConditionOperand and result instanceof OldIR::ConditionOperand or
|
||||
newTag instanceof UnmodeledUseOperand and result instanceof OldIR::UnmodeledUseOperand or
|
||||
newTag instanceof CallTargetOperand and result instanceof OldIR::CallTargetOperand or
|
||||
newTag instanceof ThisArgumentOperand and result instanceof OldIR::ThisArgumentOperand or
|
||||
exists(PositionalArgumentOperand newArg |
|
||||
newArg = newTag and
|
||||
result.(OldIR::PositionalArgumentOperand).getArgIndex() = newArg.getArgIndex()
|
||||
)
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
@@ -49,14 +32,6 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached int getMaxCallArgIndex() {
|
||||
result = max(int argIndex |
|
||||
exists(OldIR::PositionalArgumentOperand oldOperand |
|
||||
argIndex = oldOperand.getArgIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached OldIR::Instruction getOldInstruction(Instruction instr) {
|
||||
instr.getTag() = WrappedInstructionTag(result)
|
||||
}
|
||||
@@ -135,18 +110,19 @@ cached private module Cached {
|
||||
instruction instanceof PhiInstruction // Phis always have modeled results
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldUse, OldIR::OperandTag oldTag |
|
||||
oldUse = getOldInstruction(instruction) and
|
||||
oldTag = getOldOperandTag(tag) and
|
||||
if oldUse.isMemoryOperand(oldTag) then (
|
||||
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
if oldOperand instanceof OldIR::MemoryOperand then (
|
||||
(
|
||||
if exists(Alias::getOperandMemoryAccess(oldUse, oldTag)) then (
|
||||
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
|
||||
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex |
|
||||
vvar = Alias::getOperandMemoryAccess(oldUse, oldTag).getVirtualVariable() and
|
||||
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldUse) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewInstruction(defBlock.getInstruction(defIndex))
|
||||
@@ -162,25 +138,25 @@ cached private module Cached {
|
||||
// `UnmodeledUse` instruction.
|
||||
exists(OldIR::Instruction oldDefinition |
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperand and
|
||||
oldDefinition = oldUse.getOperand(oldTag) and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
oldDefinition = oldOperand.getDefinitionInstruction() and
|
||||
not exists(Alias::getResultMemoryAccess(oldDefinition)) and
|
||||
result = getNewInstruction(oldDefinition)
|
||||
)
|
||||
)
|
||||
else
|
||||
result = getNewInstruction(oldUse.getOperand(oldTag))
|
||||
) or
|
||||
result = getPhiInstructionOperand(instruction.(PhiInstruction), tag.(PhiOperand))
|
||||
result = getNewInstruction(oldOperand.getDefinitionInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionOperand(PhiInstruction instr, PhiOperand tag) {
|
||||
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
|
||||
IRBlock newPredecessorBlock) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
|
||||
hasPhiNode(vvar, phiBlock) and
|
||||
predBlock = phiBlock.getAPredecessor() and
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
tag.getPredecessorBlock() = getNewBlock(predBlock) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
|
||||
if defIndex >= 0 then
|
||||
@@ -277,7 +253,7 @@ cached private module Cached {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock block, int index) {
|
||||
exists(Alias::MemoryAccess access |
|
||||
access = Alias::getOperandMemoryAccess(use, _) and
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand()) and
|
||||
block.getInstruction(index) = use and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
|
||||
@@ -2,7 +2,8 @@ import SimpleSSAInternal
|
||||
import cpp
|
||||
import Alias
|
||||
private import InputIR
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
private newtype TVirtualVariable =
|
||||
MkVirtualVariable(IRVariable var) {
|
||||
@@ -69,15 +70,15 @@ Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
|
||||
MemoryAccess getResultMemoryAccess(Instruction instr) {
|
||||
exists(IRVariable var |
|
||||
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and
|
||||
resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(),
|
||||
var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
|
||||
MemoryAccess getOperandMemoryAccess(Instruction instr, OperandTag tag) {
|
||||
MemoryAccess getOperandMemoryAccess(Operand operand) {
|
||||
exists(IRVariable var |
|
||||
instr.getOperandMemoryAccess(tag) instanceof IndirectMemoryAccess and
|
||||
resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import FunctionIR
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import OperandTag
|
||||
import Operand
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ private import internal.IRInternal
|
||||
import FunctionIR
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import OperandTag
|
||||
import Operand
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
|
||||
class InstructionTag = Construction::InstructionTagType;
|
||||
|
||||
@@ -22,21 +23,21 @@ module InstructionSanity {
|
||||
exists(Opcode opcode |
|
||||
opcode = instr.getOpcode() and
|
||||
(
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperand or
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperandTag or
|
||||
(
|
||||
opcode instanceof BinaryOpcode and
|
||||
(
|
||||
tag instanceof LeftOperand or
|
||||
tag instanceof RightOperand
|
||||
tag instanceof LeftOperandTag or
|
||||
tag instanceof RightOperandTag
|
||||
)
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperand or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof LoadStoreAddressOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperand or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperand or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperand or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperand or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperand
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -45,26 +46,34 @@ module InstructionSanity {
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, OperandTag tag) {
|
||||
expectsOperand(instr, tag) and not exists(instr.getOperand(tag))
|
||||
expectsOperand(instr, tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperand) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperand) and
|
||||
not (instr instanceof PhiInstruction and tag instanceof PhiOperand)
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(Instruction instr, OperandTag tag) {
|
||||
strictcount(instr.getOperand(tag)) > 1 and
|
||||
not tag instanceof UnmodeledUseOperand
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +83,7 @@ module InstructionSanity {
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiOperand operand |
|
||||
exists(instr.getOperand(operand)) and
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
@@ -98,14 +107,13 @@ module InstructionSanity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `op` consumes an operand `operand` that was defined in
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Instruction op, Instruction operand, OperandTag tag
|
||||
) {
|
||||
operand = op.getOperand(tag) and
|
||||
operand.getFunctionIR() != op.getFunctionIR()
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getInstruction() = instr and
|
||||
operand.getDefinitionInstruction() = defInstr and
|
||||
instr.getFunctionIR() != defInstr.getFunctionIR()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,20 +254,6 @@ class Instruction extends Construction::TInstruction {
|
||||
result = getResultId() + "(" + getResultTypeString() + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the specified operand, suitable for display in IR
|
||||
* dumps. This consists of the result ID of the instruction consumed by the
|
||||
* operand, plus a label identifying the operand kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
string getOperandString(OperandTag tag) {
|
||||
exists(Instruction operand |
|
||||
operand = getOperand(tag) and
|
||||
result = tag.getLabel() + operand.getResultId()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operands of this instruction, suitable for
|
||||
* display in IR dumps.
|
||||
@@ -267,9 +261,9 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `func:r3_4, this:r3_5`
|
||||
*/
|
||||
string getOperandsString() {
|
||||
result = concat(OperandTag tag, Instruction operand |
|
||||
operand = getOperand(tag) |
|
||||
tag.getLabel() + operand.getResultId(), ", " order by tag.getSortOrder()
|
||||
result = concat(Operand operand |
|
||||
operand = getAnOperand() |
|
||||
operand.getDumpString(), ", " order by operand.getDumpSortOrder()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -333,7 +327,6 @@ class Instruction extends Construction::TInstruction {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -397,35 +390,19 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that produced the value of the specified source
|
||||
* operand.
|
||||
* Gets all direct uses of the result of this instruction.
|
||||
*/
|
||||
final Instruction getOperand(OperandTag tag) {
|
||||
result = Construction::getInstructionOperand(this, tag)
|
||||
final Operand getAUse() {
|
||||
result.getDefinitionInstruction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all instructions consumed by this instruction's operands.
|
||||
* Gets all of this instruction's operands.
|
||||
*/
|
||||
final Instruction getAnOperand() {
|
||||
result = getOperand(_)
|
||||
final Operand getAnOperand() {
|
||||
result.getInstruction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this instruction has a memory operand with the specified tag.
|
||||
*/
|
||||
final predicate isMemoryOperand(OperandTag tag) {
|
||||
exists(getOperandMemoryAccess(tag))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the specified operand. Holds
|
||||
* only for memory operands.
|
||||
*/
|
||||
MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this instruction produces a memory result.
|
||||
*/
|
||||
@@ -461,9 +438,7 @@ class Instruction extends Construction::TInstruction {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not exists(Instruction useInstr, UnmodeledUseOperand useTag |
|
||||
this = useInstr.getOperand(useTag)
|
||||
)
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +577,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
|
||||
final Instruction getObjectAddress() {
|
||||
result = getOperand(unaryOperand())
|
||||
result = getAnOperand().(UnaryOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,12 +615,7 @@ class ReturnValueInstruction extends ReturnInstruction {
|
||||
}
|
||||
|
||||
final Instruction getReturnValue() {
|
||||
result = getOperand(returnValueOperand())
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(ReturnValueOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
result = getAnOperand().(ReturnValueOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +625,7 @@ class CopyInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getSourceValue() {
|
||||
result = getOperand(copySourceOperand())
|
||||
result = getAnOperand().(CopySourceOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,13 +640,8 @@ class LoadInstruction extends CopyInstruction {
|
||||
opcode instanceof Opcode::Load
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(CopySourceOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
final Instruction getSourceAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +655,7 @@ class StoreInstruction extends CopyInstruction {
|
||||
}
|
||||
|
||||
final Instruction getDestinationAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,7 +665,7 @@ class ConditionalBranchInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getCondition() {
|
||||
result = getOperand(conditionOperand())
|
||||
result = getAnOperand().(ConditionOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getTrueSuccessor() {
|
||||
@@ -758,11 +723,11 @@ class BinaryInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getLeftOperand() {
|
||||
result = getOperand(leftOperand())
|
||||
result = getAnOperand().(LeftOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getRightOperand() {
|
||||
result = getOperand(rightOperand())
|
||||
result = getAnOperand().(RightOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,7 +838,7 @@ class UnaryInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getOperand() {
|
||||
result = getOperand(unaryOperand())
|
||||
result = getAnOperand().(UnaryOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,6 +957,7 @@ class RelationalInstruction extends CompareInstruction {
|
||||
RelationalInstruction() {
|
||||
opcode instanceof RelationalOpcode
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand on the "greater" (or "greater-or-equal") side
|
||||
* of this relational instruction, that is, the side that is larger
|
||||
@@ -1011,6 +977,7 @@ class RelationalInstruction extends CompareInstruction {
|
||||
Instruction getLesserOperand() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this relational instruction is strict (is not an "or-equal" instruction).
|
||||
*/
|
||||
@@ -1097,7 +1064,7 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getExpression() {
|
||||
result = getOperand(conditionOperand())
|
||||
result = getAnOperand().(ConditionOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getACaseSuccessor() {
|
||||
@@ -1117,7 +1084,7 @@ class CallInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getCallTarget() {
|
||||
result = getOperand(callTargetOperand())
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,24 +1105,18 @@ class ThrowValueInstruction extends ThrowInstruction {
|
||||
opcode instanceof Opcode::ThrowValue
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(ExceptionOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the address of the exception thrown by this instruction.
|
||||
*/
|
||||
final Instruction getExceptionAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception thrown by this instruction.
|
||||
*/
|
||||
final Instruction getException() {
|
||||
result = getOperand(exceptionOperand())
|
||||
result = getAnOperand().(ExceptionOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,11 +1197,6 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
override string getOperandsString() {
|
||||
result = "mu*"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(UnmodeledUseOperand))) and
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class PhiInstruction extends Instruction {
|
||||
@@ -1248,11 +1204,6 @@ class PhiInstruction extends Instruction {
|
||||
opcode instanceof Opcode::Phi
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(PhiOperand))) and
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
|
||||
360
cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
Normal file
360
cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
Normal file
@@ -0,0 +1,360 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
|
||||
private newtype TOperand =
|
||||
TNonPhiOperand(Instruction instr, OperandTag tag, Instruction defInstr) {
|
||||
defInstr = Construction::getInstructionOperandDefinition(instr, tag)
|
||||
} or
|
||||
TPhiOperand(PhiInstruction instr, Instruction defInstr, IRBlock predecessorBlock) {
|
||||
defInstr = Construction::getPhiInstructionOperandDefinition(instr, predecessorBlock)
|
||||
}
|
||||
|
||||
/**
|
||||
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
|
||||
*/
|
||||
class Operand extends TOperand {
|
||||
string toString() {
|
||||
result = "Operand"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
Instruction getInstruction() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` whose result is the value of the operand.
|
||||
*/
|
||||
Instruction getDefinitionInstruction() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a prefix to use when dumping the operand in an operand list.
|
||||
*/
|
||||
string getDumpLabel() {
|
||||
result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
|
||||
* result ID of the instruction consumed by the operand, plus a label identifying the operand
|
||||
* kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
final string getDumpString() {
|
||||
result = getDumpLabel() + getDefinitionInstruction().getResultId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order in which the operand should be sorted in the operand list.
|
||||
*/
|
||||
int getDumpSortOrder() {
|
||||
result = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand. Holds only for memory operands.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
* value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2`
|
||||
* is `r1`.
|
||||
*/
|
||||
final AddressOperand getAddressOperand() {
|
||||
getMemoryAccess() instanceof IndirectMemoryAccess and
|
||||
result.getInstruction() = getInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that is not an operand of a `PhiInstruction`.
|
||||
*/
|
||||
class NonPhiOperand extends Operand, TNonPhiOperand {
|
||||
Instruction instr;
|
||||
Instruction defInstr;
|
||||
OperandTag tag;
|
||||
|
||||
NonPhiOperand() {
|
||||
this = TNonPhiOperand(instr, tag, defInstr)
|
||||
}
|
||||
|
||||
override final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override final Instruction getDefinitionInstruction() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = tag.getLabel()
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = tag.getSortOrder()
|
||||
}
|
||||
|
||||
final OperandTag getOperandTag() {
|
||||
result = tag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class AddressOperand extends NonPhiOperand {
|
||||
AddressOperand() {
|
||||
this = TNonPhiOperand(_, addressOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Address"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that copies this value to its
|
||||
* result (e.g. `Copy`, `Load`, `Store`).
|
||||
*/
|
||||
class CopySourceOperand extends NonPhiOperand {
|
||||
CopySourceOperand() {
|
||||
this = TNonPhiOperand(_, copySourceOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "CopySource"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
instr.getOpcode() instanceof Opcode::Load and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`).
|
||||
*/
|
||||
class UnaryOperand extends NonPhiOperand {
|
||||
UnaryOperand() {
|
||||
this = TNonPhiOperand(_, unaryOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends NonPhiOperand {
|
||||
LeftOperand() {
|
||||
this = TNonPhiOperand(_, leftOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends NonPhiOperand {
|
||||
RightOperand() {
|
||||
this = TNonPhiOperand(_, rightOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value operand of a `ReturnValue` instruction.
|
||||
*/
|
||||
class ReturnValueOperand extends NonPhiOperand {
|
||||
ReturnValueOperand() {
|
||||
this = TNonPhiOperand(_, returnValueOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ReturnValue"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class ExceptionOperand extends NonPhiOperand {
|
||||
ExceptionOperand() {
|
||||
this = TNonPhiOperand(_, exceptionOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Exception"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends NonPhiOperand {
|
||||
ConditionOperand() {
|
||||
this = TNonPhiOperand(_, conditionOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperand extends NonPhiOperand {
|
||||
UnmodeledUseOperand() {
|
||||
this = TNonPhiOperand(_, unmodeledUseOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends NonPhiOperand {
|
||||
CallTargetOperand() {
|
||||
this = TNonPhiOperand(_, callTargetOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
class ArgumentOperand extends NonPhiOperand {
|
||||
ArgumentOperand() {
|
||||
exists(ArgumentOperandTag argTag |
|
||||
this = TNonPhiOperand(_, argTag, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand {
|
||||
ThisArgumentOperand() {
|
||||
this = TNonPhiOperand(_, thisArgumentOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ThisArgument"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand {
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperand() {
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
this = TNonPhiOperand(_, argTag, _) and
|
||||
argIndex = argTag.getArgIndex()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiOperand extends Operand, TPhiOperand {
|
||||
PhiInstruction instr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
|
||||
PhiOperand() {
|
||||
this = TPhiOperand(instr, defInstr, predecessorBlock)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Phi"
|
||||
}
|
||||
|
||||
override final PhiInstruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override final Instruction getDefinitionInstruction() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = 11 + getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the predecessor block from which this value comes.
|
||||
*/
|
||||
final IRBlock getPredecessorBlock() {
|
||||
result = predecessorBlock
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that reads a value from memory.
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
MemoryOperand() {
|
||||
exists(getMemoryAccess())
|
||||
}
|
||||
}
|
||||
@@ -1,312 +0,0 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import cpp
|
||||
|
||||
private newtype TOperandTag =
|
||||
TLoadStoreAddressOperand() or
|
||||
TCopySourceOperand() or
|
||||
TUnaryOperand() or
|
||||
TLeftOperand() or
|
||||
TRightOperand() or
|
||||
TReturnValueOperand() or
|
||||
TExceptionOperand() or
|
||||
TConditionOperand() or
|
||||
TUnmodeledUseOperand() or
|
||||
TCallTargetOperand() or
|
||||
TThisArgumentOperand() or
|
||||
TPositionalArgumentOperand(int argIndex) {
|
||||
argIndex in [0..Construction::getMaxCallArgIndex()] or
|
||||
exists(BuiltInOperation op |
|
||||
exists(op.getChild(argIndex))
|
||||
)
|
||||
} or
|
||||
TPhiOperand(IRBlock predecessorBlock) {
|
||||
exists(PhiInstruction phi |
|
||||
predecessorBlock = Construction::getPhiInstructionBlockStart(phi).getBlock().getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the kind of operand on an instruction. Each `Instruction` has at
|
||||
* most one operand of any single `OperandTag`. The set of `OperandTag`s used by
|
||||
* an `Instruction` is determined by the instruction's opcode.
|
||||
*/
|
||||
abstract class OperandTag extends TOperandTag {
|
||||
abstract string toString();
|
||||
|
||||
abstract int getSortOrder();
|
||||
|
||||
string getLabel() {
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
// Note: individual subtypes are listed in the order that the operands should
|
||||
// appear in the operand list of the instruction when printing.
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class LoadStoreAddressOperand extends OperandTag, TLoadStoreAddressOperand {
|
||||
override final string toString() {
|
||||
result = "LoadStoreAddress"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 0
|
||||
}
|
||||
}
|
||||
|
||||
LoadStoreAddressOperand loadStoreAddressOperand() {
|
||||
result = TLoadStoreAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that copies this value to its
|
||||
* result (e.g. `Copy`, `Load`, `Store`).
|
||||
*/
|
||||
class CopySourceOperand extends OperandTag, TCopySourceOperand {
|
||||
override final string toString() {
|
||||
result = "CopySource"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
CopySourceOperand copySourceOperand() {
|
||||
result = TCopySourceOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`).
|
||||
*/
|
||||
class UnaryOperand extends OperandTag, TUnaryOperand {
|
||||
override final string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 2
|
||||
}
|
||||
}
|
||||
|
||||
UnaryOperand unaryOperand() {
|
||||
result = TUnaryOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends OperandTag, TLeftOperand {
|
||||
override final string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 3
|
||||
}
|
||||
}
|
||||
|
||||
LeftOperand leftOperand() {
|
||||
result = TLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends OperandTag, TRightOperand {
|
||||
override final string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 4
|
||||
}
|
||||
}
|
||||
|
||||
RightOperand rightOperand() {
|
||||
result = TRightOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value operand of a `ReturnValue` instruction.
|
||||
*/
|
||||
class ReturnValueOperand extends OperandTag, TReturnValueOperand {
|
||||
override final string toString() {
|
||||
result = "ReturnValue"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 5
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValueOperand returnValueOperand() {
|
||||
result = TReturnValueOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class ExceptionOperand extends OperandTag, TExceptionOperand {
|
||||
override final string toString() {
|
||||
result = "Exception"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 6
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionOperand exceptionOperand() {
|
||||
result = TExceptionOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends OperandTag, TConditionOperand {
|
||||
override final string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 7
|
||||
}
|
||||
}
|
||||
|
||||
ConditionOperand conditionOperand() {
|
||||
result = TConditionOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperand extends OperandTag, TUnmodeledUseOperand {
|
||||
override final string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 8
|
||||
}
|
||||
}
|
||||
|
||||
UnmodeledUseOperand unmodeledUseOperand() {
|
||||
result = TUnmodeledUseOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends OperandTag, TCallTargetOperand {
|
||||
override final string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 9
|
||||
}
|
||||
}
|
||||
|
||||
CallTargetOperand callTargetOperand() {
|
||||
result = TCallTargetOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
abstract class ArgumentOperand extends OperandTag {
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand, TThisArgumentOperand {
|
||||
ThisArgumentOperand() {
|
||||
this = TThisArgumentOperand()
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Arg(this)"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 10
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "this:"
|
||||
}
|
||||
}
|
||||
|
||||
ThisArgumentOperand thisArgumentOperand() {
|
||||
result = TThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand,
|
||||
TPositionalArgumentOperand {
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperand() {
|
||||
this = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 11 + argIndex
|
||||
}
|
||||
|
||||
final int getArgIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
PositionalArgumentOperand positionalArgumentOperand(int argIndex) {
|
||||
result = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of an SSA `Phi` instruction.
|
||||
*/
|
||||
class PhiOperand extends OperandTag, TPhiOperand {
|
||||
IRBlock predecessorBlock;
|
||||
|
||||
PhiOperand() {
|
||||
this = TPhiOperand(predecessorBlock)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Phi"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 11 + getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
final IRBlock getPredecessorBlock() {
|
||||
result = predecessorBlock
|
||||
}
|
||||
}
|
||||
|
||||
PhiOperand phiOperand(IRBlock predecessorBlock) {
|
||||
result = TPhiOperand(predecessorBlock)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
import IRBlockConstruction as BlockConstruction
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
@@ -37,14 +38,6 @@ cached private module Cached {
|
||||
exists(getTranslatedFunction(func))
|
||||
}
|
||||
|
||||
cached int getMaxCallArgIndex() {
|
||||
result = max(int argIndex |
|
||||
exists(FunctionCall call |
|
||||
exists(call.getArgument(argIndex))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached newtype TInstruction =
|
||||
MkInstruction(FunctionIR funcIR, Opcode opcode, Locatable ast,
|
||||
InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
@@ -93,11 +86,16 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) {
|
||||
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
|
||||
result = getInstructionTranslatedElement(instruction).getInstructionOperand(
|
||||
instruction.getTag(), tag)
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionOperandDefinition(Instruction instruction,
|
||||
IRBlock predecessorBlock) {
|
||||
none()
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -209,7 +210,7 @@ class TranslatedLogicalOrExpr extends TranslatedBinaryLogicalOperation {
|
||||
}
|
||||
|
||||
class TranslatedValueCondition extends TranslatedCondition,
|
||||
TTranslatedValueCondition {
|
||||
TTranslatedValueCondition {
|
||||
TranslatedValueCondition() {
|
||||
this = TTranslatedValueCondition(expr)
|
||||
}
|
||||
@@ -223,7 +224,7 @@ class TranslatedValueCondition extends TranslatedCondition,
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType instanceof VoidType and
|
||||
@@ -236,7 +237,7 @@ class TranslatedValueCondition extends TranslatedCondition,
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
EdgeKind kind) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
(
|
||||
(
|
||||
@@ -251,9 +252,9 @@ class TranslatedValueCondition extends TranslatedCondition,
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
operandTag instanceof ConditionOperand and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = getValueExpr().getResult()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -157,7 +158,7 @@ class TranslatedUninitializedVariable extends
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
@@ -209,16 +210,16 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
(
|
||||
tag = ConditionValueTrueStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ConditionValueTrueTempAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInstruction(ConditionValueTrueConstantTag())
|
||||
)
|
||||
)
|
||||
@@ -227,11 +228,11 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
tag = ConditionValueFalseStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ConditionValueFalseTempAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInstruction(ConditionValueFalseConstantTag())
|
||||
)
|
||||
)
|
||||
@@ -240,11 +241,11 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ConditionValueResultTempAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -315,7 +316,7 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
Type resultType, boolean isGLValue) {
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = expr.getType().getUnspecifiedType() and
|
||||
@@ -326,7 +327,7 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
EdgeKind kind) {
|
||||
tag = LoadTag() and
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
@@ -345,11 +346,11 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
tag = LoadTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -404,7 +405,7 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr {
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -478,16 +479,16 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
(
|
||||
tag = CrementLoadTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -496,11 +497,11 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
tag = CrementOpTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = getInstruction(CrementLoadTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getInstruction(CrementConstantTag())
|
||||
)
|
||||
)
|
||||
@@ -509,11 +510,11 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
tag = CrementStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInstruction(CrementOpTag())
|
||||
)
|
||||
)
|
||||
@@ -670,11 +671,11 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
|
||||
tag = OnlyInstructionTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = getBaseOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getOffsetOperand().getResult()
|
||||
)
|
||||
)
|
||||
@@ -722,7 +723,7 @@ abstract class TranslatedTransparentExpr extends TranslatedNonConstantExpr {
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -804,7 +805,7 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr {
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
@@ -857,7 +858,7 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -891,7 +892,7 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
|
||||
@@ -987,7 +988,7 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr {
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -1100,13 +1101,13 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getOperand().getResult() and
|
||||
if getOpcode() instanceof Opcode::CopyValue then
|
||||
operandTag instanceof CopySourceOperand
|
||||
operandTag instanceof CopySourceOperandTag
|
||||
else
|
||||
operandTag instanceof UnaryOperand
|
||||
operandTag instanceof UnaryOperandTag
|
||||
}
|
||||
|
||||
override final Opcode getOpcode() {
|
||||
@@ -1172,9 +1173,9 @@ abstract class TranslatedSingleInstructionConversion extends TranslatedConversio
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getOperand().getResult()
|
||||
}
|
||||
|
||||
@@ -1313,11 +1314,11 @@ class TranslatedBoolConversion extends TranslatedConversion {
|
||||
tag = BoolConversionCompareTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = getOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getInstruction(BoolConversionConstantTag())
|
||||
)
|
||||
)
|
||||
@@ -1381,21 +1382,21 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
|
||||
tag = OnlyInstructionTag() and
|
||||
if swapOperandsOnOp() then (
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getLeftOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = getRightOperand().getResult()
|
||||
)
|
||||
)
|
||||
else (
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = getLeftOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getRightOperand().getResult()
|
||||
)
|
||||
)
|
||||
@@ -1551,11 +1552,11 @@ class TranslatedAssignExpr extends TranslatedAssignment {
|
||||
tag = AssignmentStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getLeftOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getRightOperand().getResult()
|
||||
)
|
||||
)
|
||||
@@ -1714,11 +1715,11 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
tag = AssignOperationLoadTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getLeftOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -1726,21 +1727,21 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
(
|
||||
leftOperandNeedsConversion() and
|
||||
tag = AssignOperationConvertLeftTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getInstruction(AssignOperationLoadTag())
|
||||
) or
|
||||
(
|
||||
tag = AssignOperationOpTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
if leftOperandNeedsConversion() then
|
||||
result = getInstruction(AssignOperationConvertLeftTag())
|
||||
else
|
||||
result = getInstruction(AssignOperationLoadTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getRightOperand().getResult()
|
||||
)
|
||||
)
|
||||
@@ -1748,18 +1749,18 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
(
|
||||
leftOperandNeedsConversion() and
|
||||
tag = AssignOperationConvertResultTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getInstruction(AssignOperationOpTag())
|
||||
) or
|
||||
(
|
||||
tag = AssignmentStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getLeftOperand().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getStoredValue()
|
||||
)
|
||||
)
|
||||
@@ -1828,14 +1829,14 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
tag = CallTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof CallTargetOperand and
|
||||
operandTag instanceof CallTargetOperandTag and
|
||||
result = getCallTargetResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof ThisArgumentOperand and
|
||||
operandTag instanceof ThisArgumentOperandTag and
|
||||
result = getQualifierResult()
|
||||
) or
|
||||
exists(PositionalArgumentOperand argTag |
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
argTag = operandTag and
|
||||
result = getArgument(argTag.getArgIndex()).getResult()
|
||||
)
|
||||
@@ -2071,13 +2072,13 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
(
|
||||
tag = AllocationSizeTag() and
|
||||
(
|
||||
operandTag instanceof LeftOperand and result = getInstruction(AllocationExtentConvertTag()) or
|
||||
operandTag instanceof RightOperand and result = getInstruction(AllocationElementSizeTag())
|
||||
operandTag instanceof LeftOperandTag and result = getInstruction(AllocationExtentConvertTag()) or
|
||||
operandTag instanceof RightOperandTag and result = getInstruction(AllocationElementSizeTag())
|
||||
)
|
||||
) or
|
||||
(
|
||||
tag = AllocationExtentConvertTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getExtent().getResult()
|
||||
)
|
||||
}
|
||||
@@ -2322,7 +2323,7 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr,
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getTranslatedFunction(destruction.getEnclosingFunction()).getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
@@ -2433,7 +2434,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr,
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
not resultIsVoid() and
|
||||
(
|
||||
(
|
||||
@@ -2441,11 +2442,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr,
|
||||
tag = ConditionValueTrueStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ConditionValueTrueTempAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getThen().getResult()
|
||||
)
|
||||
)
|
||||
@@ -2455,11 +2456,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr,
|
||||
tag = ConditionValueFalseStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ConditionValueFalseTempAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getElse().getResult()
|
||||
)
|
||||
)
|
||||
@@ -2468,11 +2469,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr,
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ConditionValueResultTempAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -2648,11 +2649,11 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr,
|
||||
tag = ThrowTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof ExceptionOperand and
|
||||
operandTag instanceof ExceptionOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -2864,7 +2865,7 @@ abstract class TranslatedNewOrNewArrayExpr extends TranslatedNonConstantExpr,
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getAllocatorCall().getResult()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
@@ -210,10 +211,10 @@ class TranslatedFunction extends TranslatedElement,
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
(
|
||||
tag = UnmodeledUseTag() and
|
||||
operandTag instanceof UnmodeledUseOperand and
|
||||
operandTag instanceof UnmodeledUseOperandTag and
|
||||
result.getFunction() = func and
|
||||
result.hasMemoryResult()
|
||||
) or
|
||||
@@ -222,11 +223,11 @@ class TranslatedFunction extends TranslatedElement,
|
||||
not getReturnType() instanceof VoidType and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ReturnValueAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof ReturnValueOperand and
|
||||
operandTag instanceof ReturnValueOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -374,11 +375,11 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -205,15 +206,15 @@ class TranslatedSimpleDirectInitialization extends
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getContext().getTargetAddress()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInitializer().getResult()
|
||||
)
|
||||
)
|
||||
@@ -332,11 +333,11 @@ class TranslatedStringLiteralInitialization extends
|
||||
tag = InitializerLoadStringTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInitializer().getResult()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
@@ -345,11 +346,11 @@ class TranslatedStringLiteralInitialization extends
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getContext().getTargetAddress()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInstruction(InitializerLoadStringTag())
|
||||
)
|
||||
)
|
||||
@@ -358,11 +359,11 @@ class TranslatedStringLiteralInitialization extends
|
||||
tag = ZeroPadStringElementAddressTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = getContext().getTargetAddress()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getInstruction(ZeroPadStringElementIndexTag())
|
||||
)
|
||||
)
|
||||
@@ -371,11 +372,11 @@ class TranslatedStringLiteralInitialization extends
|
||||
tag = ZeroPadStringStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ZeroPadStringElementAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInstruction(ZeroPadStringConstantTag())
|
||||
)
|
||||
)
|
||||
@@ -450,7 +451,7 @@ class TranslatedConstructorInitialization extends
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -514,9 +515,9 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = getFieldAddressTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getParent().(InitializationContext).getTargetAddress()
|
||||
}
|
||||
|
||||
@@ -633,17 +634,17 @@ class TranslatedFieldValueInitialization extends
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
result = TranslatedFieldInitialization.super.getInstructionOperand(tag, operandTag) or
|
||||
(
|
||||
tag = getFieldDefaultValueStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(getFieldAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInstruction(getFieldDefaultValueTag())
|
||||
)
|
||||
)
|
||||
@@ -723,15 +724,15 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = getElementAddressTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LeftOperand and
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = getParent().(InitializationContext).getTargetAddress()
|
||||
) or
|
||||
(
|
||||
operandTag instanceof RightOperand and
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = getInstruction(getElementIndexTag())
|
||||
)
|
||||
)
|
||||
@@ -882,17 +883,17 @@ class TranslatedElementValueInitialization extends
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
result = TranslatedElementInitialization.super.getInstructionOperand(tag, operandTag) or
|
||||
(
|
||||
tag = getElementDefaultValueStoreTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof LoadStoreAddressOperand and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(getElementAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof CopySourceOperand and
|
||||
operandTag instanceof CopySourceOperandTag and
|
||||
result = getInstruction(getElementDefaultValueTag())
|
||||
)
|
||||
)
|
||||
@@ -981,9 +982,9 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperand and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
private import TranslatedDeclarationEntry
|
||||
@@ -732,7 +733,7 @@ class TranslatedSwitchStmt extends TranslatedStmt {
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
tag = SwitchBranchTag() and
|
||||
operandTag instanceof ConditionOperand and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = getExpr().getResult()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import FunctionIR
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import OperandTag
|
||||
import Operand
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ private import internal.IRInternal
|
||||
import FunctionIR
|
||||
import IRBlock
|
||||
import IRVariable
|
||||
import OperandTag
|
||||
import Operand
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
|
||||
class InstructionTag = Construction::InstructionTagType;
|
||||
|
||||
@@ -22,21 +23,21 @@ module InstructionSanity {
|
||||
exists(Opcode opcode |
|
||||
opcode = instr.getOpcode() and
|
||||
(
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperand or
|
||||
opcode instanceof UnaryOpcode and tag instanceof UnaryOperandTag or
|
||||
(
|
||||
opcode instanceof BinaryOpcode and
|
||||
(
|
||||
tag instanceof LeftOperand or
|
||||
tag instanceof RightOperand
|
||||
tag instanceof LeftOperandTag or
|
||||
tag instanceof RightOperandTag
|
||||
)
|
||||
) or
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperand or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof LoadStoreAddressOperand or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperand or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperand or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperand or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperand or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperand
|
||||
opcode instanceof CopyOpcode and tag instanceof CopySourceOperandTag or
|
||||
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag or
|
||||
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag or
|
||||
opcode instanceof Opcode::ReturnValue and tag instanceof ReturnValueOperandTag or
|
||||
opcode instanceof Opcode::ThrowValue and tag instanceof ExceptionOperandTag or
|
||||
opcode instanceof Opcode::UnmodeledUse and tag instanceof UnmodeledUseOperandTag or
|
||||
opcode instanceof Opcode::Call and tag instanceof CallTargetOperandTag
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -45,26 +46,34 @@ module InstructionSanity {
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, OperandTag tag) {
|
||||
expectsOperand(instr, tag) and not exists(instr.getOperand(tag))
|
||||
expectsOperand(instr, tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag) and
|
||||
not expectsOperand(instr, tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperand) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperand) and
|
||||
not (instr instanceof PhiInstruction and tag instanceof PhiOperand)
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (instr instanceof BuiltInInstruction and tag instanceof PositionalArgumentOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(Instruction instr, OperandTag tag) {
|
||||
strictcount(instr.getOperand(tag)) > 1 and
|
||||
not tag instanceof UnmodeledUseOperand
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +83,7 @@ module InstructionSanity {
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiOperand operand |
|
||||
exists(instr.getOperand(operand)) and
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
@@ -98,14 +107,13 @@ module InstructionSanity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `op` consumes an operand `operand` that was defined in
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(
|
||||
Instruction op, Instruction operand, OperandTag tag
|
||||
) {
|
||||
operand = op.getOperand(tag) and
|
||||
operand.getFunctionIR() != op.getFunctionIR()
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getInstruction() = instr and
|
||||
operand.getDefinitionInstruction() = defInstr and
|
||||
instr.getFunctionIR() != defInstr.getFunctionIR()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,20 +254,6 @@ class Instruction extends Construction::TInstruction {
|
||||
result = getResultId() + "(" + getResultTypeString() + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the specified operand, suitable for display in IR
|
||||
* dumps. This consists of the result ID of the instruction consumed by the
|
||||
* operand, plus a label identifying the operand kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
string getOperandString(OperandTag tag) {
|
||||
exists(Instruction operand |
|
||||
operand = getOperand(tag) and
|
||||
result = tag.getLabel() + operand.getResultId()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operands of this instruction, suitable for
|
||||
* display in IR dumps.
|
||||
@@ -267,9 +261,9 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `func:r3_4, this:r3_5`
|
||||
*/
|
||||
string getOperandsString() {
|
||||
result = concat(OperandTag tag, Instruction operand |
|
||||
operand = getOperand(tag) |
|
||||
tag.getLabel() + operand.getResultId(), ", " order by tag.getSortOrder()
|
||||
result = concat(Operand operand |
|
||||
operand = getAnOperand() |
|
||||
operand.getDumpString(), ", " order by operand.getDumpSortOrder()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -333,7 +327,6 @@ class Instruction extends Construction::TInstruction {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -397,35 +390,19 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that produced the value of the specified source
|
||||
* operand.
|
||||
* Gets all direct uses of the result of this instruction.
|
||||
*/
|
||||
final Instruction getOperand(OperandTag tag) {
|
||||
result = Construction::getInstructionOperand(this, tag)
|
||||
final Operand getAUse() {
|
||||
result.getDefinitionInstruction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all instructions consumed by this instruction's operands.
|
||||
* Gets all of this instruction's operands.
|
||||
*/
|
||||
final Instruction getAnOperand() {
|
||||
result = getOperand(_)
|
||||
final Operand getAnOperand() {
|
||||
result.getInstruction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this instruction has a memory operand with the specified tag.
|
||||
*/
|
||||
final predicate isMemoryOperand(OperandTag tag) {
|
||||
exists(getOperandMemoryAccess(tag))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the specified operand. Holds
|
||||
* only for memory operands.
|
||||
*/
|
||||
MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this instruction produces a memory result.
|
||||
*/
|
||||
@@ -461,9 +438,7 @@ class Instruction extends Construction::TInstruction {
|
||||
// Register results are always in SSA form.
|
||||
not hasMemoryResult() or
|
||||
// An unmodeled result will have a use on the `UnmodeledUse` instruction.
|
||||
not exists(Instruction useInstr, UnmodeledUseOperand useTag |
|
||||
this = useInstr.getOperand(useTag)
|
||||
)
|
||||
not (getAUse() instanceof UnmodeledUseOperand)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +577,7 @@ class FieldAddressInstruction extends FieldInstruction {
|
||||
}
|
||||
|
||||
final Instruction getObjectAddress() {
|
||||
result = getOperand(unaryOperand())
|
||||
result = getAnOperand().(UnaryOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,12 +615,7 @@ class ReturnValueInstruction extends ReturnInstruction {
|
||||
}
|
||||
|
||||
final Instruction getReturnValue() {
|
||||
result = getOperand(returnValueOperand())
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(ReturnValueOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
result = getAnOperand().(ReturnValueOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +625,7 @@ class CopyInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getSourceValue() {
|
||||
result = getOperand(copySourceOperand())
|
||||
result = getAnOperand().(CopySourceOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,13 +640,8 @@ class LoadInstruction extends CopyInstruction {
|
||||
opcode instanceof Opcode::Load
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(CopySourceOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
final Instruction getSourceAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +655,7 @@ class StoreInstruction extends CopyInstruction {
|
||||
}
|
||||
|
||||
final Instruction getDestinationAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,7 +665,7 @@ class ConditionalBranchInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getCondition() {
|
||||
result = getOperand(conditionOperand())
|
||||
result = getAnOperand().(ConditionOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getTrueSuccessor() {
|
||||
@@ -758,11 +723,11 @@ class BinaryInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getLeftOperand() {
|
||||
result = getOperand(leftOperand())
|
||||
result = getAnOperand().(LeftOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getRightOperand() {
|
||||
result = getOperand(rightOperand())
|
||||
result = getAnOperand().(RightOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,7 +838,7 @@ class UnaryInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getOperand() {
|
||||
result = getOperand(unaryOperand())
|
||||
result = getAnOperand().(UnaryOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,6 +957,7 @@ class RelationalInstruction extends CompareInstruction {
|
||||
RelationalInstruction() {
|
||||
opcode instanceof RelationalOpcode
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand on the "greater" (or "greater-or-equal") side
|
||||
* of this relational instruction, that is, the side that is larger
|
||||
@@ -1011,6 +977,7 @@ class RelationalInstruction extends CompareInstruction {
|
||||
Instruction getLesserOperand() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this relational instruction is strict (is not an "or-equal" instruction).
|
||||
*/
|
||||
@@ -1097,7 +1064,7 @@ class SwitchInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getExpression() {
|
||||
result = getOperand(conditionOperand())
|
||||
result = getAnOperand().(ConditionOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
final Instruction getACaseSuccessor() {
|
||||
@@ -1117,7 +1084,7 @@ class CallInstruction extends Instruction {
|
||||
}
|
||||
|
||||
final Instruction getCallTarget() {
|
||||
result = getOperand(callTargetOperand())
|
||||
result = getAnOperand().(CallTargetOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,24 +1105,18 @@ class ThrowValueInstruction extends ThrowInstruction {
|
||||
opcode instanceof Opcode::ThrowValue
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(ExceptionOperand))) and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the address of the exception thrown by this instruction.
|
||||
*/
|
||||
final Instruction getExceptionAddress() {
|
||||
result = getOperand(loadStoreAddressOperand())
|
||||
result = getAnOperand().(AddressOperand).getDefinitionInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception thrown by this instruction.
|
||||
*/
|
||||
final Instruction getException() {
|
||||
result = getOperand(exceptionOperand())
|
||||
result = getAnOperand().(ExceptionOperand).getDefinitionInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1236,11 +1197,6 @@ class UnmodeledUseInstruction extends Instruction {
|
||||
override string getOperandsString() {
|
||||
result = "mu*"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(UnmodeledUseOperand))) and
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
class PhiInstruction extends Instruction {
|
||||
@@ -1248,11 +1204,6 @@ class PhiInstruction extends Instruction {
|
||||
opcode instanceof Opcode::Phi
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getOperandMemoryAccess(OperandTag tag) {
|
||||
exists(this.getOperand(tag.(PhiOperand))) and
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getResultMemoryAccess() {
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
|
||||
private newtype TOperand =
|
||||
TNonPhiOperand(Instruction instr, OperandTag tag, Instruction defInstr) {
|
||||
defInstr = Construction::getInstructionOperandDefinition(instr, tag)
|
||||
} or
|
||||
TPhiOperand(PhiInstruction instr, Instruction defInstr, IRBlock predecessorBlock) {
|
||||
defInstr = Construction::getPhiInstructionOperandDefinition(instr, predecessorBlock)
|
||||
}
|
||||
|
||||
/**
|
||||
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
|
||||
*/
|
||||
class Operand extends TOperand {
|
||||
string toString() {
|
||||
result = "Operand"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
Instruction getInstruction() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Instruction` whose result is the value of the operand.
|
||||
*/
|
||||
Instruction getDefinitionInstruction() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a prefix to use when dumping the operand in an operand list.
|
||||
*/
|
||||
string getDumpLabel() {
|
||||
result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing this operand, suitable for display in IR dumps. This consists of the
|
||||
* result ID of the instruction consumed by the operand, plus a label identifying the operand
|
||||
* kind.
|
||||
*
|
||||
* For example: `this:r3_5`
|
||||
*/
|
||||
final string getDumpString() {
|
||||
result = getDumpLabel() + getDefinitionInstruction().getResultId()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order in which the operand should be sorted in the operand list.
|
||||
*/
|
||||
int getDumpSortOrder() {
|
||||
result = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand. Holds only for memory operands.
|
||||
*/
|
||||
MemoryAccessKind getMemoryAccess() {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operand that holds the memory address from which the current operand loads its
|
||||
* value, if any. For example, in `r3 = Load r1, m2`, the result of `getAddressOperand()` for `m2`
|
||||
* is `r1`.
|
||||
*/
|
||||
final AddressOperand getAddressOperand() {
|
||||
getMemoryAccess() instanceof IndirectMemoryAccess and
|
||||
result.getInstruction() = getInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that is not an operand of a `PhiInstruction`.
|
||||
*/
|
||||
class NonPhiOperand extends Operand, TNonPhiOperand {
|
||||
Instruction instr;
|
||||
Instruction defInstr;
|
||||
OperandTag tag;
|
||||
|
||||
NonPhiOperand() {
|
||||
this = TNonPhiOperand(instr, tag, defInstr)
|
||||
}
|
||||
|
||||
override final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override final Instruction getDefinitionInstruction() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = tag.getLabel()
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = tag.getSortOrder()
|
||||
}
|
||||
|
||||
final OperandTag getOperandTag() {
|
||||
result = tag
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class AddressOperand extends NonPhiOperand {
|
||||
AddressOperand() {
|
||||
this = TNonPhiOperand(_, addressOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Address"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that copies this value to its
|
||||
* result (e.g. `Copy`, `Load`, `Store`).
|
||||
*/
|
||||
class CopySourceOperand extends NonPhiOperand {
|
||||
CopySourceOperand() {
|
||||
this = TNonPhiOperand(_, copySourceOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "CopySource"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
instr.getOpcode() instanceof Opcode::Load and
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`).
|
||||
*/
|
||||
class UnaryOperand extends NonPhiOperand {
|
||||
UnaryOperand() {
|
||||
this = TNonPhiOperand(_, unaryOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends NonPhiOperand {
|
||||
LeftOperand() {
|
||||
this = TNonPhiOperand(_, leftOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends NonPhiOperand {
|
||||
RightOperand() {
|
||||
this = TNonPhiOperand(_, rightOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value operand of a `ReturnValue` instruction.
|
||||
*/
|
||||
class ReturnValueOperand extends NonPhiOperand {
|
||||
ReturnValueOperand() {
|
||||
this = TNonPhiOperand(_, returnValueOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ReturnValue"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class ExceptionOperand extends NonPhiOperand {
|
||||
ExceptionOperand() {
|
||||
this = TNonPhiOperand(_, exceptionOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Exception"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof IndirectMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends NonPhiOperand {
|
||||
ConditionOperand() {
|
||||
this = TNonPhiOperand(_, conditionOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperand extends NonPhiOperand {
|
||||
UnmodeledUseOperand() {
|
||||
this = TNonPhiOperand(_, unmodeledUseOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof UnmodeledMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends NonPhiOperand {
|
||||
CallTargetOperand() {
|
||||
this = TNonPhiOperand(_, callTargetOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
class ArgumentOperand extends NonPhiOperand {
|
||||
ArgumentOperand() {
|
||||
exists(ArgumentOperandTag argTag |
|
||||
this = TNonPhiOperand(_, argTag, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand {
|
||||
ThisArgumentOperand() {
|
||||
this = TNonPhiOperand(_, thisArgumentOperand(), _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "ThisArgument"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand {
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperand() {
|
||||
exists(PositionalArgumentOperandTag argTag |
|
||||
this = TNonPhiOperand(_, argTag, _) and
|
||||
argIndex = argTag.getArgIndex()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of a `PhiInstruction`.
|
||||
*/
|
||||
class PhiOperand extends Operand, TPhiOperand {
|
||||
PhiInstruction instr;
|
||||
Instruction defInstr;
|
||||
IRBlock predecessorBlock;
|
||||
|
||||
PhiOperand() {
|
||||
this = TPhiOperand(instr, defInstr, predecessorBlock)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Phi"
|
||||
}
|
||||
|
||||
override final PhiInstruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override final Instruction getDefinitionInstruction() {
|
||||
result = defInstr
|
||||
}
|
||||
|
||||
override final int getDumpSortOrder() {
|
||||
result = 11 + getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getDumpLabel() {
|
||||
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the predecessor block from which this value comes.
|
||||
*/
|
||||
final IRBlock getPredecessorBlock() {
|
||||
result = predecessorBlock
|
||||
}
|
||||
|
||||
override final MemoryAccessKind getMemoryAccess() {
|
||||
result instanceof PhiMemoryAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand that reads a value from memory.
|
||||
*/
|
||||
class MemoryOperand extends Operand {
|
||||
MemoryOperand() {
|
||||
exists(getMemoryAccess())
|
||||
}
|
||||
}
|
||||
@@ -1,312 +0,0 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import cpp
|
||||
|
||||
private newtype TOperandTag =
|
||||
TLoadStoreAddressOperand() or
|
||||
TCopySourceOperand() or
|
||||
TUnaryOperand() or
|
||||
TLeftOperand() or
|
||||
TRightOperand() or
|
||||
TReturnValueOperand() or
|
||||
TExceptionOperand() or
|
||||
TConditionOperand() or
|
||||
TUnmodeledUseOperand() or
|
||||
TCallTargetOperand() or
|
||||
TThisArgumentOperand() or
|
||||
TPositionalArgumentOperand(int argIndex) {
|
||||
argIndex in [0..Construction::getMaxCallArgIndex()] or
|
||||
exists(BuiltInOperation op |
|
||||
exists(op.getChild(argIndex))
|
||||
)
|
||||
} or
|
||||
TPhiOperand(IRBlock predecessorBlock) {
|
||||
exists(PhiInstruction phi |
|
||||
predecessorBlock = Construction::getPhiInstructionBlockStart(phi).getBlock().getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the kind of operand on an instruction. Each `Instruction` has at
|
||||
* most one operand of any single `OperandTag`. The set of `OperandTag`s used by
|
||||
* an `Instruction` is determined by the instruction's opcode.
|
||||
*/
|
||||
abstract class OperandTag extends TOperandTag {
|
||||
abstract string toString();
|
||||
|
||||
abstract int getSortOrder();
|
||||
|
||||
string getLabel() {
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
// Note: individual subtypes are listed in the order that the operands should
|
||||
// appear in the operand list of the instruction when printing.
|
||||
|
||||
/**
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class LoadStoreAddressOperand extends OperandTag, TLoadStoreAddressOperand {
|
||||
override final string toString() {
|
||||
result = "LoadStoreAddress"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 0
|
||||
}
|
||||
}
|
||||
|
||||
LoadStoreAddressOperand loadStoreAddressOperand() {
|
||||
result = TLoadStoreAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that copies this value to its
|
||||
* result (e.g. `Copy`, `Load`, `Store`).
|
||||
*/
|
||||
class CopySourceOperand extends OperandTag, TCopySourceOperand {
|
||||
override final string toString() {
|
||||
result = "CopySource"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
CopySourceOperand copySourceOperand() {
|
||||
result = TCopySourceOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`).
|
||||
*/
|
||||
class UnaryOperand extends OperandTag, TUnaryOperand {
|
||||
override final string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 2
|
||||
}
|
||||
}
|
||||
|
||||
UnaryOperand unaryOperand() {
|
||||
result = TUnaryOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends OperandTag, TLeftOperand {
|
||||
override final string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 3
|
||||
}
|
||||
}
|
||||
|
||||
LeftOperand leftOperand() {
|
||||
result = TLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends OperandTag, TRightOperand {
|
||||
override final string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 4
|
||||
}
|
||||
}
|
||||
|
||||
RightOperand rightOperand() {
|
||||
result = TRightOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value operand of a `ReturnValue` instruction.
|
||||
*/
|
||||
class ReturnValueOperand extends OperandTag, TReturnValueOperand {
|
||||
override final string toString() {
|
||||
result = "ReturnValue"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 5
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValueOperand returnValueOperand() {
|
||||
result = TReturnValueOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class ExceptionOperand extends OperandTag, TExceptionOperand {
|
||||
override final string toString() {
|
||||
result = "Exception"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 6
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionOperand exceptionOperand() {
|
||||
result = TExceptionOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends OperandTag, TConditionOperand {
|
||||
override final string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 7
|
||||
}
|
||||
}
|
||||
|
||||
ConditionOperand conditionOperand() {
|
||||
result = TConditionOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperand extends OperandTag, TUnmodeledUseOperand {
|
||||
override final string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 8
|
||||
}
|
||||
}
|
||||
|
||||
UnmodeledUseOperand unmodeledUseOperand() {
|
||||
result = TUnmodeledUseOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends OperandTag, TCallTargetOperand {
|
||||
override final string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 9
|
||||
}
|
||||
}
|
||||
|
||||
CallTargetOperand callTargetOperand() {
|
||||
result = TCallTargetOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call. This includes both
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
abstract class ArgumentOperand extends OperandTag {
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand, TThisArgumentOperand {
|
||||
ThisArgumentOperand() {
|
||||
this = TThisArgumentOperand()
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Arg(this)"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 10
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "this:"
|
||||
}
|
||||
}
|
||||
|
||||
ThisArgumentOperand thisArgumentOperand() {
|
||||
result = TThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand,
|
||||
TPositionalArgumentOperand {
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperand() {
|
||||
this = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Arg(" + argIndex + ")"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 11 + argIndex
|
||||
}
|
||||
|
||||
final int getArgIndex() {
|
||||
result = argIndex
|
||||
}
|
||||
}
|
||||
|
||||
PositionalArgumentOperand positionalArgumentOperand(int argIndex) {
|
||||
result = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of an SSA `Phi` instruction.
|
||||
*/
|
||||
class PhiOperand extends OperandTag, TPhiOperand {
|
||||
IRBlock predecessorBlock;
|
||||
|
||||
PhiOperand() {
|
||||
this = TPhiOperand(predecessorBlock)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Phi"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 11 + getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
final IRBlock getPredecessorBlock() {
|
||||
result = predecessorBlock
|
||||
}
|
||||
}
|
||||
|
||||
PhiOperand phiOperand(IRBlock predecessorBlock) {
|
||||
result = TPhiOperand(predecessorBlock)
|
||||
}
|
||||
@@ -46,18 +46,20 @@ private IntValue getFieldBitOffset(Field field) {
|
||||
* not result in any address held in that operand from escaping beyond the
|
||||
* instruction.
|
||||
*/
|
||||
predicate operandIsConsumedWithoutEscaping(Instruction instr, OperandTag tag) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
(
|
||||
// The source/destination address of a Load/Store does not escape (but the
|
||||
// loaded/stored value could).
|
||||
tag instanceof LoadStoreAddressOperand or
|
||||
// Neither operand of a Compare escapes.
|
||||
instr instanceof CompareInstruction or
|
||||
// Neither operand of a PointerDiff escapes.
|
||||
instr instanceof PointerDiffInstruction or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultType() instanceof BoolType
|
||||
predicate operandIsConsumedWithoutEscaping(Operand operand) {
|
||||
// The source/destination address of a Load/Store does not escape (but the
|
||||
// loaded/stored value could).
|
||||
operand instanceof AddressOperand or
|
||||
exists (Instruction instr |
|
||||
instr = operand.getInstruction() and
|
||||
(
|
||||
// Neither operand of a Compare escapes.
|
||||
instr instanceof CompareInstruction or
|
||||
// Neither operand of a PointerDiff escapes.
|
||||
instr instanceof PointerDiffInstruction or
|
||||
// Converting an address to a `bool` does not escape the address.
|
||||
instr.(ConvertInstruction).getResultType() instanceof BoolType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,43 +98,44 @@ IntValue getPointerBitOffset(PointerOffsetInstruction instr) {
|
||||
* `bitOffset`. If the address is propagated, but the offset is not known to be
|
||||
* a constant, then `bitOffset` is unknown.
|
||||
*/
|
||||
predicate operandIsPropagated(Instruction instr, OperandTag tag,
|
||||
IntValue bitOffset) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
(
|
||||
// Converting to a non-virtual base class adds the offset of the base class.
|
||||
exists(ConvertToBaseInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||
) or
|
||||
// Converting to a derived class subtracts the offset of the base class.
|
||||
exists(ConvertToDerivedInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
|
||||
) or
|
||||
// Converting to a virtual base class adds an unknown offset.
|
||||
predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
|
||||
exists(Instruction instr |
|
||||
instr = operand.getInstruction() and
|
||||
(
|
||||
instr instanceof ConvertToVirtualBaseInstruction and
|
||||
bitOffset = Ints::unknown()
|
||||
) or
|
||||
// Conversion to another pointer type propagates the source address.
|
||||
exists(ConvertInstruction convert, Type resultType |
|
||||
convert = instr and
|
||||
resultType = convert.getResultType() and
|
||||
// Converting to a non-virtual base class adds the offset of the base class.
|
||||
exists(ConvertToBaseInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
|
||||
) or
|
||||
// Converting to a derived class subtracts the offset of the base class.
|
||||
exists(ConvertToDerivedInstruction convert |
|
||||
convert = instr and
|
||||
bitOffset = Ints::neg(Ints::mul(convert.getDerivation().getByteOffset(), 8))
|
||||
) or
|
||||
// Converting to a virtual base class adds an unknown offset.
|
||||
(
|
||||
resultType instanceof PointerType or
|
||||
resultType instanceof Class //REVIEW: Remove when all glvalues are pointers
|
||||
) and
|
||||
bitOffset = 0
|
||||
) or
|
||||
// Adding an integer to or subtracting an integer from a pointer propagates
|
||||
// the address with an offset.
|
||||
bitOffset = getPointerBitOffset(instr.(PointerOffsetInstruction)) or
|
||||
// Computing a field address from a pointer propagates the address plus the
|
||||
// offset of the field.
|
||||
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or
|
||||
// A copy propagates the source value.
|
||||
tag instanceof CopySourceOperand and bitOffset = 0
|
||||
instr instanceof ConvertToVirtualBaseInstruction and
|
||||
bitOffset = Ints::unknown()
|
||||
) or
|
||||
// Conversion to another pointer type propagates the source address.
|
||||
exists(ConvertInstruction convert, Type resultType |
|
||||
convert = instr and
|
||||
resultType = convert.getResultType() and
|
||||
(
|
||||
resultType instanceof PointerType or
|
||||
resultType instanceof Class //REVIEW: Remove when all glvalues are pointers
|
||||
) and
|
||||
bitOffset = 0
|
||||
) or
|
||||
// Adding an integer to or subtracting an integer from a pointer propagates
|
||||
// the address with an offset.
|
||||
bitOffset = getPointerBitOffset(instr.(PointerOffsetInstruction)) or
|
||||
// Computing a field address from a pointer propagates the address plus the
|
||||
// offset of the field.
|
||||
bitOffset = getFieldBitOffset(instr.(FieldAddressInstruction).getField()) or
|
||||
// A copy propagates the source value.
|
||||
operand instanceof CopySourceOperand and bitOffset = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,16 +143,15 @@ predicate operandIsPropagated(Instruction instr, OperandTag tag,
|
||||
* Holds if any address held in operand number `tag` of instruction `instr`
|
||||
* escapes outside the domain of the analysis.
|
||||
*/
|
||||
predicate operandEscapes(Instruction instr, OperandTag tag) {
|
||||
exists(instr.getOperand(tag)) and
|
||||
predicate operandEscapes(Operand operand) {
|
||||
// Conservatively assume that the address escapes unless one of the following
|
||||
// holds:
|
||||
not (
|
||||
// The operand is used in a way that does not escape the instruction
|
||||
operandIsConsumedWithoutEscaping(instr, tag) or
|
||||
operandIsConsumedWithoutEscaping(operand) or
|
||||
// The address is propagated to the result of the instruction, but that
|
||||
// result does not itself escape.
|
||||
operandIsPropagated(instr, tag, _) and not resultEscapes(instr)
|
||||
operandIsPropagated(operand, _) and not resultEscapes(operand.getInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -159,10 +161,7 @@ predicate operandEscapes(Instruction instr, OperandTag tag) {
|
||||
*/
|
||||
predicate resultEscapes(Instruction instr) {
|
||||
// The result escapes if it has at least one use that escapes.
|
||||
exists(Instruction useInstr, OperandTag useOperandTag |
|
||||
useInstr.getOperand(useOperandTag) = instr and
|
||||
operandEscapes(useInstr, useOperandTag)
|
||||
)
|
||||
operandEscapes(instr.getAUse())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -203,12 +202,12 @@ predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset)
|
||||
instr.(VariableAddressInstruction).getVariable() = var and
|
||||
bitOffset = 0
|
||||
) or
|
||||
exists(OperandTag operandTag, IntValue originalBitOffset,
|
||||
IntValue propagatedBitOffset |
|
||||
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
|
||||
operand = instr.getAnOperand() and
|
||||
// If an operand is propagated, then the result points to the same variable,
|
||||
// offset by the bit offset from the propagation.
|
||||
resultPointsTo(instr.getOperand(operandTag), var, originalBitOffset) and
|
||||
operandIsPropagated(instr, operandTag, propagatedBitOffset) and
|
||||
resultPointsTo(operand.getDefinitionInstruction(), var, originalBitOffset) and
|
||||
operandIsPropagated(operand, propagatedBitOffset) and
|
||||
bitOffset = Ints::add(originalBitOffset, propagatedBitOffset)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,30 +1,13 @@
|
||||
import SSAConstructionInternal
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
import NewIR
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import NewIR
|
||||
import IRBlockConstruction as BlockConstruction
|
||||
|
||||
import Cached
|
||||
cached private module Cached {
|
||||
|
||||
private OldIR::OperandTag getOldOperandTag(OperandTag newTag) {
|
||||
newTag instanceof LoadStoreAddressOperand and result instanceof OldIR::LoadStoreAddressOperand or
|
||||
newTag instanceof CopySourceOperand and result instanceof OldIR::CopySourceOperand or
|
||||
newTag instanceof UnaryOperand and result instanceof OldIR::UnaryOperand or
|
||||
newTag instanceof LeftOperand and result instanceof OldIR::LeftOperand or
|
||||
newTag instanceof RightOperand and result instanceof OldIR::RightOperand or
|
||||
newTag instanceof ReturnValueOperand and result instanceof OldIR::ReturnValueOperand or
|
||||
newTag instanceof ExceptionOperand and result instanceof OldIR::ExceptionOperand or
|
||||
newTag instanceof ConditionOperand and result instanceof OldIR::ConditionOperand or
|
||||
newTag instanceof UnmodeledUseOperand and result instanceof OldIR::UnmodeledUseOperand or
|
||||
newTag instanceof CallTargetOperand and result instanceof OldIR::CallTargetOperand or
|
||||
newTag instanceof ThisArgumentOperand and result instanceof OldIR::ThisArgumentOperand or
|
||||
exists(PositionalArgumentOperand newArg |
|
||||
newArg = newTag and
|
||||
result.(OldIR::PositionalArgumentOperand).getArgIndex() = newArg.getArgIndex()
|
||||
)
|
||||
}
|
||||
|
||||
private IRBlock getNewBlock(OldIR::IRBlock oldBlock) {
|
||||
result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
|
||||
}
|
||||
@@ -49,14 +32,6 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached int getMaxCallArgIndex() {
|
||||
result = max(int argIndex |
|
||||
exists(OldIR::PositionalArgumentOperand oldOperand |
|
||||
argIndex = oldOperand.getArgIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached OldIR::Instruction getOldInstruction(Instruction instr) {
|
||||
instr.getTag() = WrappedInstructionTag(result)
|
||||
}
|
||||
@@ -135,18 +110,19 @@ cached private module Cached {
|
||||
instruction instanceof PhiInstruction // Phis always have modeled results
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldUse, OldIR::OperandTag oldTag |
|
||||
oldUse = getOldInstruction(instruction) and
|
||||
oldTag = getOldOperandTag(tag) and
|
||||
if oldUse.isMemoryOperand(oldTag) then (
|
||||
cached Instruction getInstructionOperandDefinition(Instruction instruction, OperandTag tag) {
|
||||
exists(OldIR::Instruction oldInstruction, OldIR::NonPhiOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instruction) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
if oldOperand instanceof OldIR::MemoryOperand then (
|
||||
(
|
||||
if exists(Alias::getOperandMemoryAccess(oldUse, oldTag)) then (
|
||||
if exists(Alias::getOperandMemoryAccess(oldOperand)) then (
|
||||
exists(OldIR::IRBlock useBlock, int useRank, Alias::VirtualVariable vvar,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex |
|
||||
vvar = Alias::getOperandMemoryAccess(oldUse, oldTag).getVirtualVariable() and
|
||||
vvar = Alias::getOperandMemoryAccess(oldOperand).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldUse) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstruction) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
if defIndex >= 0 then
|
||||
result = getNewInstruction(defBlock.getInstruction(defIndex))
|
||||
@@ -162,25 +138,25 @@ cached private module Cached {
|
||||
// `UnmodeledUse` instruction.
|
||||
exists(OldIR::Instruction oldDefinition |
|
||||
instruction instanceof UnmodeledUseInstruction and
|
||||
tag instanceof UnmodeledUseOperand and
|
||||
oldDefinition = oldUse.getOperand(oldTag) and
|
||||
tag instanceof UnmodeledUseOperandTag and
|
||||
oldDefinition = oldOperand.getDefinitionInstruction() and
|
||||
not exists(Alias::getResultMemoryAccess(oldDefinition)) and
|
||||
result = getNewInstruction(oldDefinition)
|
||||
)
|
||||
)
|
||||
else
|
||||
result = getNewInstruction(oldUse.getOperand(oldTag))
|
||||
) or
|
||||
result = getPhiInstructionOperand(instruction.(PhiInstruction), tag.(PhiOperand))
|
||||
result = getNewInstruction(oldOperand.getDefinitionInstruction())
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getPhiInstructionOperand(PhiInstruction instr, PhiOperand tag) {
|
||||
cached Instruction getPhiInstructionOperandDefinition(PhiInstruction instr,
|
||||
IRBlock newPredecessorBlock) {
|
||||
exists(Alias::VirtualVariable vvar, OldIR::IRBlock phiBlock,
|
||||
OldIR::IRBlock defBlock, int defRank, int defIndex, OldIR::IRBlock predBlock |
|
||||
hasPhiNode(vvar, phiBlock) and
|
||||
predBlock = phiBlock.getAPredecessor() and
|
||||
instr.getTag() = PhiTag(vvar, phiBlock) and
|
||||
tag.getPredecessorBlock() = getNewBlock(predBlock) and
|
||||
newPredecessorBlock = getNewBlock(predBlock) and
|
||||
hasDefinitionAtRank(vvar, defBlock, defRank, defIndex) and
|
||||
definitionReachesEndOfBlock(vvar, defBlock, defRank, predBlock) and
|
||||
if defIndex >= 0 then
|
||||
@@ -277,7 +253,7 @@ cached private module Cached {
|
||||
private predicate hasUse(Alias::VirtualVariable vvar,
|
||||
OldIR::Instruction use, OldIR::IRBlock block, int index) {
|
||||
exists(Alias::MemoryAccess access |
|
||||
access = Alias::getOperandMemoryAccess(use, _) and
|
||||
access = Alias::getOperandMemoryAccess(use.getAnOperand()) and
|
||||
block.getInstruction(index) = use and
|
||||
vvar = access.getVirtualVariable()
|
||||
)
|
||||
|
||||
@@ -2,7 +2,8 @@ import SimpleSSAInternal
|
||||
import cpp
|
||||
import Alias
|
||||
private import InputIR
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
|
||||
private newtype TVirtualVariable =
|
||||
MkVirtualVariable(IRVariable var) {
|
||||
@@ -69,15 +70,15 @@ Overlap getOverlap(MemoryAccess def, MemoryAccess use) {
|
||||
MemoryAccess getResultMemoryAccess(Instruction instr) {
|
||||
exists(IRVariable var |
|
||||
instr.getResultMemoryAccess() instanceof IndirectMemoryAccess and
|
||||
resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and
|
||||
resultPointsTo(instr.getAnOperand().(AddressOperand).getDefinitionInstruction(),
|
||||
var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
|
||||
MemoryAccess getOperandMemoryAccess(Instruction instr, OperandTag tag) {
|
||||
MemoryAccess getOperandMemoryAccess(Operand operand) {
|
||||
exists(IRVariable var |
|
||||
instr.getOperandMemoryAccess(tag) instanceof IndirectMemoryAccess and
|
||||
resultPointsTo(instr.getOperand(loadStoreAddressOperand()), var, 0) and
|
||||
resultPointsTo(operand.getAddressOperand().getDefinitionInstruction(), var, 0) and
|
||||
result = getMemoryAccess(var)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
import cpp
|
||||
|
||||
private int getMaxCallArgIndex() {
|
||||
result = max(int argIndex |
|
||||
exists(FunctionCall call |
|
||||
exists(call.getArgument(argIndex))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TOperandTag =
|
||||
TLoadStoreAddressOperand() or
|
||||
TAddressOperand() or
|
||||
TCopySourceOperand() or
|
||||
TUnaryOperand() or
|
||||
TLeftOperand() or
|
||||
@@ -16,15 +21,10 @@ private newtype TOperandTag =
|
||||
TCallTargetOperand() or
|
||||
TThisArgumentOperand() or
|
||||
TPositionalArgumentOperand(int argIndex) {
|
||||
argIndex in [0..Construction::getMaxCallArgIndex()] or
|
||||
argIndex in [0..getMaxCallArgIndex()] or
|
||||
exists(BuiltInOperation op |
|
||||
exists(op.getChild(argIndex))
|
||||
)
|
||||
} or
|
||||
TPhiOperand(IRBlock predecessorBlock) {
|
||||
exists(PhiInstruction phi |
|
||||
predecessorBlock = Construction::getPhiInstructionBlockStart(phi).getBlock().getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,9 +49,9 @@ abstract class OperandTag extends TOperandTag {
|
||||
* The address operand of an instruction that loads or stores a value from
|
||||
* memory (e.g. `Load`, `Store`).
|
||||
*/
|
||||
class LoadStoreAddressOperand extends OperandTag, TLoadStoreAddressOperand {
|
||||
class AddressOperandTag extends OperandTag, TAddressOperand {
|
||||
override final string toString() {
|
||||
result = "LoadStoreAddress"
|
||||
result = "Address"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
@@ -59,15 +59,15 @@ class LoadStoreAddressOperand extends OperandTag, TLoadStoreAddressOperand {
|
||||
}
|
||||
}
|
||||
|
||||
LoadStoreAddressOperand loadStoreAddressOperand() {
|
||||
result = TLoadStoreAddressOperand()
|
||||
AddressOperandTag addressOperand() {
|
||||
result = TAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The source value operand of an instruction that copies this value to its
|
||||
* result (e.g. `Copy`, `Load`, `Store`).
|
||||
*/
|
||||
class CopySourceOperand extends OperandTag, TCopySourceOperand {
|
||||
class CopySourceOperandTag extends OperandTag, TCopySourceOperand {
|
||||
override final string toString() {
|
||||
result = "CopySource"
|
||||
}
|
||||
@@ -77,14 +77,14 @@ class CopySourceOperand extends OperandTag, TCopySourceOperand {
|
||||
}
|
||||
}
|
||||
|
||||
CopySourceOperand copySourceOperand() {
|
||||
CopySourceOperandTag copySourceOperand() {
|
||||
result = TCopySourceOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The sole operand of a unary instruction (e.g. `Convert`, `Negate`).
|
||||
*/
|
||||
class UnaryOperand extends OperandTag, TUnaryOperand {
|
||||
class UnaryOperandTag extends OperandTag, TUnaryOperand {
|
||||
override final string toString() {
|
||||
result = "Unary"
|
||||
}
|
||||
@@ -94,14 +94,14 @@ class UnaryOperand extends OperandTag, TUnaryOperand {
|
||||
}
|
||||
}
|
||||
|
||||
UnaryOperand unaryOperand() {
|
||||
UnaryOperandTag unaryOperand() {
|
||||
result = TUnaryOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The left operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class LeftOperand extends OperandTag, TLeftOperand {
|
||||
class LeftOperandTag extends OperandTag, TLeftOperand {
|
||||
override final string toString() {
|
||||
result = "Left"
|
||||
}
|
||||
@@ -111,14 +111,14 @@ class LeftOperand extends OperandTag, TLeftOperand {
|
||||
}
|
||||
}
|
||||
|
||||
LeftOperand leftOperand() {
|
||||
LeftOperandTag leftOperand() {
|
||||
result = TLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The right operand of a binary instruction (e.g. `Add`, `CompareEQ`).
|
||||
*/
|
||||
class RightOperand extends OperandTag, TRightOperand {
|
||||
class RightOperandTag extends OperandTag, TRightOperand {
|
||||
override final string toString() {
|
||||
result = "Right"
|
||||
}
|
||||
@@ -128,14 +128,14 @@ class RightOperand extends OperandTag, TRightOperand {
|
||||
}
|
||||
}
|
||||
|
||||
RightOperand rightOperand() {
|
||||
RightOperandTag rightOperand() {
|
||||
result = TRightOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The return value operand of a `ReturnValue` instruction.
|
||||
*/
|
||||
class ReturnValueOperand extends OperandTag, TReturnValueOperand {
|
||||
class ReturnValueOperandTag extends OperandTag, TReturnValueOperand {
|
||||
override final string toString() {
|
||||
result = "ReturnValue"
|
||||
}
|
||||
@@ -145,14 +145,14 @@ class ReturnValueOperand extends OperandTag, TReturnValueOperand {
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValueOperand returnValueOperand() {
|
||||
ReturnValueOperandTag returnValueOperand() {
|
||||
result = TReturnValueOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception thrown by a `ThrowValue` instruction.
|
||||
*/
|
||||
class ExceptionOperand extends OperandTag, TExceptionOperand {
|
||||
class ExceptionOperandTag extends OperandTag, TExceptionOperand {
|
||||
override final string toString() {
|
||||
result = "Exception"
|
||||
}
|
||||
@@ -162,14 +162,14 @@ class ExceptionOperand extends OperandTag, TExceptionOperand {
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionOperand exceptionOperand() {
|
||||
ExceptionOperandTag exceptionOperand() {
|
||||
result = TExceptionOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition operand of a `ConditionalBranch` or `Switch` instruction.
|
||||
*/
|
||||
class ConditionOperand extends OperandTag, TConditionOperand {
|
||||
class ConditionOperandTag extends OperandTag, TConditionOperand {
|
||||
override final string toString() {
|
||||
result = "Condition"
|
||||
}
|
||||
@@ -179,7 +179,7 @@ class ConditionOperand extends OperandTag, TConditionOperand {
|
||||
}
|
||||
}
|
||||
|
||||
ConditionOperand conditionOperand() {
|
||||
ConditionOperandTag conditionOperand() {
|
||||
result = TConditionOperand()
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ ConditionOperand conditionOperand() {
|
||||
* An operand of the special `UnmodeledUse` instruction, representing a value
|
||||
* whose set of uses is unknown.
|
||||
*/
|
||||
class UnmodeledUseOperand extends OperandTag, TUnmodeledUseOperand {
|
||||
class UnmodeledUseOperandTag extends OperandTag, TUnmodeledUseOperand {
|
||||
override final string toString() {
|
||||
result = "UnmodeledUse"
|
||||
}
|
||||
@@ -197,14 +197,14 @@ class UnmodeledUseOperand extends OperandTag, TUnmodeledUseOperand {
|
||||
}
|
||||
}
|
||||
|
||||
UnmodeledUseOperand unmodeledUseOperand() {
|
||||
UnmodeledUseOperandTag unmodeledUseOperand() {
|
||||
result = TUnmodeledUseOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand representing the target function of an `Call` instruction.
|
||||
*/
|
||||
class CallTargetOperand extends OperandTag, TCallTargetOperand {
|
||||
class CallTargetOperandTag extends OperandTag, TCallTargetOperand {
|
||||
override final string toString() {
|
||||
result = "CallTarget"
|
||||
}
|
||||
@@ -214,7 +214,7 @@ class CallTargetOperand extends OperandTag, TCallTargetOperand {
|
||||
}
|
||||
}
|
||||
|
||||
CallTargetOperand callTargetOperand() {
|
||||
CallTargetOperandTag callTargetOperand() {
|
||||
result = TCallTargetOperand()
|
||||
}
|
||||
|
||||
@@ -223,15 +223,15 @@ CallTargetOperand callTargetOperand() {
|
||||
* positional arguments (represented by `PositionalArgumentOperand`) and the
|
||||
* implicit `this` argument, if any (represented by `ThisArgumentOperand`).
|
||||
*/
|
||||
abstract class ArgumentOperand extends OperandTag {
|
||||
abstract class ArgumentOperandTag extends OperandTag {
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing the implicit 'this' argument to a member function
|
||||
* call.
|
||||
*/
|
||||
class ThisArgumentOperand extends ArgumentOperand, TThisArgumentOperand {
|
||||
ThisArgumentOperand() {
|
||||
class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand {
|
||||
ThisArgumentOperandTag() {
|
||||
this = TThisArgumentOperand()
|
||||
}
|
||||
|
||||
@@ -248,18 +248,18 @@ class ThisArgumentOperand extends ArgumentOperand, TThisArgumentOperand {
|
||||
}
|
||||
}
|
||||
|
||||
ThisArgumentOperand thisArgumentOperand() {
|
||||
ThisArgumentOperandTag thisArgumentOperand() {
|
||||
result = TThisArgumentOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand representing an argument to a function call.
|
||||
*/
|
||||
class PositionalArgumentOperand extends ArgumentOperand,
|
||||
class PositionalArgumentOperandTag extends ArgumentOperandTag,
|
||||
TPositionalArgumentOperand {
|
||||
int argIndex;
|
||||
|
||||
PositionalArgumentOperand() {
|
||||
PositionalArgumentOperandTag() {
|
||||
this = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
@@ -276,37 +276,6 @@ class PositionalArgumentOperand extends ArgumentOperand,
|
||||
}
|
||||
}
|
||||
|
||||
PositionalArgumentOperand positionalArgumentOperand(int argIndex) {
|
||||
PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
|
||||
result = TPositionalArgumentOperand(argIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand of an SSA `Phi` instruction.
|
||||
*/
|
||||
class PhiOperand extends OperandTag, TPhiOperand {
|
||||
IRBlock predecessorBlock;
|
||||
|
||||
PhiOperand() {
|
||||
this = TPhiOperand(predecessorBlock)
|
||||
}
|
||||
|
||||
override final string toString() {
|
||||
result = "Phi"
|
||||
}
|
||||
|
||||
override final int getSortOrder() {
|
||||
result = 11 + getPredecessorBlock().getDisplayIndex()
|
||||
}
|
||||
|
||||
override final string getLabel() {
|
||||
result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":"
|
||||
}
|
||||
|
||||
final IRBlock getPredecessorBlock() {
|
||||
result = predecessorBlock
|
||||
}
|
||||
}
|
||||
|
||||
PhiOperand phiOperand(IRBlock predecessorBlock) {
|
||||
result = TPhiOperand(predecessorBlock)
|
||||
}
|
||||
@@ -19,8 +19,8 @@ IntValue getConstantValue(Instruction instr) {
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue()) or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = max(Instruction operand | operand = phi.getAnOperand() | getConstantValue(operand)) and
|
||||
result = min(Instruction operand | operand = phi.getAnOperand() | getConstantValue(operand))
|
||||
result = max(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction())) and
|
||||
result = min(PhiOperand operand | operand = phi.getAnOperand() | getConstantValue(operand.getDefinitionInstruction()))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1746,3 +1746,28 @@ class SystemNetWebUtilityFlow extends LibraryTypeDataFlow, SystemNetWebUtility {
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `StringValues` class used in many .NET Core libraries. Requires special `LibraryTypeDataFlow` flow.
|
||||
*/
|
||||
class StringValues extends Struct {
|
||||
StringValues() { this.hasQualifiedName("Microsoft.Extensions.Primitives", "StringValues") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom flow through StringValues.StringValues library class
|
||||
*/
|
||||
class StringValuesFlow extends LibraryTypeDataFlow, StringValues {
|
||||
override predicate callableFlow(
|
||||
CallableFlowSource source, CallableFlowSink sink, SourceDeclarationCallable c,
|
||||
boolean preservesValue
|
||||
) {
|
||||
c = any(Callable ca | this = ca.getDeclaringType()) and
|
||||
(
|
||||
source = any(CallableFlowSourceArg a) or
|
||||
source = any(CallableFlowSourceQualifier q)
|
||||
) and
|
||||
sink = any(CallableFlowSinkReturn r) and
|
||||
preservesValue = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ private import semmle.code.csharp.frameworks.system.web.Services
|
||||
private import semmle.code.csharp.frameworks.system.web.ui.WebControls
|
||||
private import semmle.code.csharp.frameworks.WCF
|
||||
private import semmle.code.csharp.frameworks.microsoft.Owin
|
||||
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
|
||||
|
||||
/** A data flow source of remote user input. */
|
||||
abstract class RemoteFlowSource extends DataFlow::Node {
|
||||
@@ -28,9 +29,8 @@ class AspNetQueryStringMember extends Member {
|
||||
t instanceof SystemWebHttpRequestClass or
|
||||
t instanceof SystemNetHttpListenerRequestClass or
|
||||
t instanceof SystemWebHttpRequestBaseClass
|
||||
|
|
||||
this = t.getProperty(getHttpRequestFlowPropertyNames())
|
||||
or
|
||||
|
|
||||
this = t.getProperty(getHttpRequestFlowPropertyNames()) or
|
||||
this.(Field).getType() = t or
|
||||
this.(Property).getType() = t or
|
||||
this.(Callable).getReturnType() = t
|
||||
@@ -61,7 +61,7 @@ class AspNetQueryStringRemoteFlowSource extends AspNetRemoteFlowSource, DataFlow
|
||||
t instanceof SystemWebHttpRequestClass or
|
||||
t instanceof SystemNetHttpListenerRequestClass or
|
||||
t instanceof SystemWebHttpRequestBaseClass
|
||||
|
|
||||
|
|
||||
// A request object can be indexed, so taint the object as well
|
||||
this.getExpr().getType() = t
|
||||
)
|
||||
@@ -69,39 +69,35 @@ class AspNetQueryStringRemoteFlowSource extends AspNetRemoteFlowSource, DataFlow
|
||||
this.getExpr() = any(AspNetQueryStringMember m).getAnAccess()
|
||||
}
|
||||
|
||||
override
|
||||
string getSourceType() { result = "ASP.NET query string" }
|
||||
override string getSourceType() { result = "ASP.NET query string" }
|
||||
}
|
||||
|
||||
/** A data flow source of remote user input (ASP.NET unvalidated request data). */
|
||||
class AspNetUnvalidatedQueryStringRemoteFlowSource extends AspNetRemoteFlowSource, DataFlow::ExprNode {
|
||||
class AspNetUnvalidatedQueryStringRemoteFlowSource extends AspNetRemoteFlowSource,
|
||||
DataFlow::ExprNode {
|
||||
AspNetUnvalidatedQueryStringRemoteFlowSource() {
|
||||
this.getExpr() = any(SystemWebUnvalidatedRequestValues c).getAProperty().getGetter().getACall() or
|
||||
this.getExpr() = any(SystemWebUnvalidatedRequestValuesBase c).getAProperty().getGetter().getACall()
|
||||
this.getExpr() = any(SystemWebUnvalidatedRequestValuesBase c)
|
||||
.getAProperty()
|
||||
.getGetter()
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override
|
||||
string getSourceType() { result = "ASP.NET unvalidated request data" }
|
||||
override string getSourceType() { result = "ASP.NET unvalidated request data" }
|
||||
}
|
||||
|
||||
/** A data flow source of remote user input (ASP.NET user input). */
|
||||
class AspNetUserInputRemoteFlowSource extends AspNetRemoteFlowSource, DataFlow::ExprNode {
|
||||
AspNetUserInputRemoteFlowSource() {
|
||||
getType() instanceof SystemWebUIWebControlsTextBoxClass
|
||||
}
|
||||
AspNetUserInputRemoteFlowSource() { getType() instanceof SystemWebUIWebControlsTextBoxClass }
|
||||
|
||||
override
|
||||
string getSourceType() { result = "ASP.NET user input" }
|
||||
override string getSourceType() { result = "ASP.NET user input" }
|
||||
}
|
||||
|
||||
/** A data flow source of remote user input (WCF based web service). */
|
||||
class WcfRemoteFlowSource extends RemoteFlowSource, DataFlow::ParameterNode {
|
||||
WcfRemoteFlowSource() {
|
||||
exists(OperationMethod om | om.getAParameter() = this.getParameter())
|
||||
}
|
||||
WcfRemoteFlowSource() { exists(OperationMethod om | om.getAParameter() = this.getParameter()) }
|
||||
|
||||
override
|
||||
string getSourceType() { result = "web service input" }
|
||||
override string getSourceType() { result = "web service input" }
|
||||
}
|
||||
|
||||
/** A data flow source of remote user input (ASP.NET web service). */
|
||||
@@ -113,8 +109,7 @@ class AspNetServiceRemoteFlowSource extends RemoteFlowSource, DataFlow::Paramete
|
||||
)
|
||||
}
|
||||
|
||||
override
|
||||
string getSourceType() { result = "ASP.NET web service input" }
|
||||
override string getSourceType() { result = "ASP.NET web service input" }
|
||||
}
|
||||
|
||||
/** A data flow source of remote user input (ASP.NET request message). */
|
||||
@@ -123,8 +118,7 @@ class SystemNetHttpRequestMessageRemoteFlowSource extends RemoteFlowSource, Data
|
||||
getType() instanceof SystemWebHttpRequestMessageClass
|
||||
}
|
||||
|
||||
override
|
||||
string getSourceType() { result = "ASP.NET request message" }
|
||||
override string getSourceType() { result = "ASP.NET request message" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,17 +130,15 @@ class MicrosoftOwinStringFlowSource extends RemoteFlowSource, DataFlow::ExprNode
|
||||
this.getExpr() = any(MicrosoftOwinString owinString).getValueProperty().getGetter().getACall()
|
||||
}
|
||||
|
||||
override
|
||||
string getSourceType() { result = "Microsoft Owin request or query string" }
|
||||
override string getSourceType() { result = "Microsoft Owin request or query string" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow source of remote user input (`Microsoft Owin IOwinRequest`).
|
||||
*/
|
||||
/** A data flow source of remote user input (`Microsoft Owin IOwinRequest`). */
|
||||
class MicrosoftOwinRequestRemoteFlowSource extends RemoteFlowSource, DataFlow::ExprNode {
|
||||
MicrosoftOwinRequestRemoteFlowSource() {
|
||||
exists(Property p, MicrosoftOwinIOwinRequestClass owinRequest |
|
||||
this.getExpr() = p.getGetter().getACall() |
|
||||
this.getExpr() = p.getGetter().getACall()
|
||||
|
|
||||
p = owinRequest.getAcceptProperty() or
|
||||
p = owinRequest.getBodyProperty() or
|
||||
p = owinRequest.getCacheControlProperty() or
|
||||
@@ -167,23 +159,62 @@ class MicrosoftOwinRequestRemoteFlowSource extends RemoteFlowSource, DataFlow::E
|
||||
)
|
||||
}
|
||||
|
||||
override
|
||||
string getSourceType() { result = "Microsoft Owin request" }
|
||||
override string getSourceType() { result = "Microsoft Owin request" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter to an Mvc controller action method, viewed as a source of remote user input.
|
||||
*/
|
||||
/** A parameter to an Mvc controller action method, viewed as a source of remote user input. */
|
||||
class ActionMethodParameter extends RemoteFlowSource, DataFlow::ParameterNode {
|
||||
ActionMethodParameter() {
|
||||
exists(Parameter p |
|
||||
p = this.getParameter() and
|
||||
p.fromSource() |
|
||||
p.fromSource()
|
||||
|
|
||||
p = any(Controller c).getAnActionMethod().getAParameter() or
|
||||
p = any(ApiController c).getAnActionMethod().getAParameter()
|
||||
)
|
||||
}
|
||||
|
||||
override
|
||||
string getSourceType() { result = "ASP.NET MVC action method parameter" }
|
||||
override string getSourceType() { result = "ASP.NET MVC action method parameter" }
|
||||
}
|
||||
|
||||
/** A data flow source of remote user input (ASP.NET Core). */
|
||||
abstract class AspNetCoreRemoteFlowSource extends RemoteFlowSource { }
|
||||
|
||||
/** A data flow source of remote user input (ASP.NET query collection). */
|
||||
class AspNetCoreQueryRemoteFlowSource extends AspNetCoreRemoteFlowSource, DataFlow::ExprNode {
|
||||
AspNetCoreQueryRemoteFlowSource() {
|
||||
exists(ValueOrRefType t |
|
||||
t instanceof MicrosoftAspNetCoreHttpHttpRequest or
|
||||
t instanceof MicrosoftAspNetCoreHttpQueryCollection or
|
||||
t instanceof MicrosoftAspNetCoreHttpQueryString
|
||||
|
|
||||
this.getExpr().(Call).getTarget().getDeclaringType() = t or
|
||||
this.asExpr().(Access).getTarget().getDeclaringType() = t
|
||||
)
|
||||
or
|
||||
exists(Call c |
|
||||
c
|
||||
.getTarget()
|
||||
.getDeclaringType()
|
||||
.hasQualifiedName("Microsoft.AspNetCore.Http", "IQueryCollection") and
|
||||
c.getTarget().getName() = "TryGetValue" and
|
||||
this.asExpr() = c.getArgumentForName("value")
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "ASP.NET Core query string" }
|
||||
}
|
||||
|
||||
/** A parameter to a `Mvc` controller action method, viewed as a source of remote user input. */
|
||||
class AspNetCoreActionMethodParameter extends RemoteFlowSource, DataFlow::ParameterNode {
|
||||
AspNetCoreActionMethodParameter() {
|
||||
exists(Parameter p |
|
||||
p = this.getParameter() and
|
||||
p.fromSource()
|
||||
|
|
||||
p = any(MicrosoftAspNetCoreMvcController c).getAnActionMethod().getAParameter()
|
||||
)
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "ASP.NET Core MVC action method parameter" }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
/** Provides classes for working with `Microsoft.AspNetCore.Mvc`. */
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.frameworks.Microsoft
|
||||
|
||||
/** The `Microsoft.AspNetCore` namespace. */
|
||||
class MicrosoftAspNetCoreNamespace extends Namespace {
|
||||
MicrosoftAspNetCoreNamespace() {
|
||||
getParentNamespace() instanceof MicrosoftNamespace and
|
||||
hasName("AspNetCore")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Mvc` namespace. */
|
||||
class MicrosoftAspNetCoreMvcNamespace extends Namespace {
|
||||
MicrosoftAspNetCoreMvcNamespace() {
|
||||
getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and
|
||||
hasName("Mvc")
|
||||
}
|
||||
}
|
||||
|
||||
/** The 'Microsoft.AspNetCore.Mvc.ViewFeatures' namespace. */
|
||||
class MicrosoftAspNetCoreMvcViewFeatures extends Namespace {
|
||||
MicrosoftAspNetCoreMvcViewFeatures() {
|
||||
getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and
|
||||
hasName("ViewFeatures")
|
||||
}
|
||||
}
|
||||
|
||||
/** An attribute whose type is in the `Microsoft.AspNetCore.Mvc` namespace. */
|
||||
class MicrosoftAspNetCoreMvcAttribute extends Attribute {
|
||||
MicrosoftAspNetCoreMvcAttribute() {
|
||||
getType().getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Microsoft.AspNetCore.Mvc.HttpPost` attribute. */
|
||||
class MicrosoftAspNetCoreMvcHttpPostAttribute extends MicrosoftAspNetCoreMvcAttribute {
|
||||
MicrosoftAspNetCoreMvcHttpPostAttribute() { getType().hasName("HttpPostAttribute") }
|
||||
}
|
||||
|
||||
/** A `Microsoft.AspNetCore.Mvc.HttpPut` attribute. */
|
||||
class MicrosoftAspNetCoreMvcHttpPutAttribute extends MicrosoftAspNetCoreMvcAttribute {
|
||||
MicrosoftAspNetCoreMvcHttpPutAttribute() { getType().hasName("HttpPutAttribute") }
|
||||
}
|
||||
|
||||
/** A `Microsoft.AspNetCore.Mvc.HttpDelete` attribute. */
|
||||
class MicrosoftAspNetCoreMvcHttpDeleteAttribute extends MicrosoftAspNetCoreMvcAttribute {
|
||||
MicrosoftAspNetCoreMvcHttpDeleteAttribute() { getType().hasName("HttpDeleteAttribute") }
|
||||
}
|
||||
|
||||
/** A `Microsoft.AspNetCore.Mvc.NonAction` attribute. */
|
||||
class MicrosoftAspNetCoreMvcNonActionAttribute extends MicrosoftAspNetCoreMvcAttribute {
|
||||
MicrosoftAspNetCoreMvcNonActionAttribute() { getType().hasName("NonActionAttribute") }
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Antiforgery` namespace. */
|
||||
class MicrosoftAspNetCoreAntiforgeryNamespace extends Namespace {
|
||||
MicrosoftAspNetCoreAntiforgeryNamespace() {
|
||||
getParentNamespace() instanceof MicrosoftAspNetCoreNamespace and
|
||||
hasName("Antiforgery")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Mvc.Filters` namespace. */
|
||||
class MicrosoftAspNetCoreMvcFilters extends Namespace {
|
||||
MicrosoftAspNetCoreMvcFilters() {
|
||||
getParentNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and
|
||||
hasName("Filters")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Mvc.Filters.IFilterMetadataInterface` interface. */
|
||||
class MicrosoftAspNetCoreMvcIFilterMetadataInterface extends Interface {
|
||||
MicrosoftAspNetCoreMvcIFilterMetadataInterface() {
|
||||
getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and
|
||||
hasName("IFilterMetadata")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.IAuthorizationFilter` interface. */
|
||||
class MicrosoftAspNetCoreIAuthorizationFilterInterface extends Interface {
|
||||
MicrosoftAspNetCoreIAuthorizationFilterInterface() {
|
||||
getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and
|
||||
hasName("IAsyncAuthorizationFilter")
|
||||
}
|
||||
|
||||
/** Gets the `OnAuthorizationAsync` method. */
|
||||
Method getOnAuthorizationMethod() { result = getAMethod("OnAuthorizationAsync") }
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.IAntiforgery` interface. */
|
||||
class MicrosoftAspNetCoreIAntiForgeryInterface extends Interface {
|
||||
MicrosoftAspNetCoreIAntiForgeryInterface() {
|
||||
getNamespace() instanceof MicrosoftAspNetCoreAntiforgeryNamespace and
|
||||
hasName("IAntiforgery")
|
||||
}
|
||||
|
||||
/** Gets the `ValidateRequestAsync` method. */
|
||||
Method getValidateMethod() { result = getAMethod("ValidateRequestAsync") }
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.DefaultAntiForgery` class, or another user-supplied class that implements `IAntiForgery`. */
|
||||
class AntiForgeryClass extends Class {
|
||||
AntiForgeryClass() { getABaseInterface*() instanceof MicrosoftAspNetCoreIAntiForgeryInterface }
|
||||
|
||||
/** Gets the `ValidateRequestAsync` method. */
|
||||
Method getValidateMethod() { result = getAMethod("ValidateRequestAsync") }
|
||||
}
|
||||
|
||||
/** An authorization filter class defined by AspNetCore or the user. */
|
||||
class AuthorizationFilterClass extends Class {
|
||||
AuthorizationFilterClass() {
|
||||
getABaseInterface*() instanceof MicrosoftAspNetCoreIAuthorizationFilterInterface
|
||||
}
|
||||
|
||||
/** Gets the `OnAuthorization` method provided by this filter. */
|
||||
Method getOnAuthorizationMethod() { result = getAMethod("OnAuthorizationAsync") }
|
||||
}
|
||||
|
||||
/** An attribute whose type has a name like `[Auto...]Validate[...]Anti[Ff]orgery[...Token]Attribute`. */
|
||||
class ValidateAntiForgeryAttribute extends Attribute {
|
||||
ValidateAntiForgeryAttribute() { getType().getName().matches("%Validate%Anti_orgery%Attribute") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that has a name like `[Auto...]Validate[...]Anti[Ff]orgery[...Token]` and implements `IFilterMetadata` interface
|
||||
* This class can be added to a collection of global `MvcOptions.Filters` collection.
|
||||
*/
|
||||
class ValidateAntiforgeryTokenAuthorizationFilter extends Class {
|
||||
ValidateAntiforgeryTokenAuthorizationFilter() {
|
||||
getABaseInterface*() instanceof MicrosoftAspNetCoreMvcIFilterMetadataInterface and
|
||||
getName().matches("%Validate%Anti_orgery%")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Mvc.Filters.FilterCollection` class. */
|
||||
class MicrosoftAspNetCoreMvcFilterCollection extends Class {
|
||||
MicrosoftAspNetCoreMvcFilterCollection() {
|
||||
getNamespace() instanceof MicrosoftAspNetCoreMvcFilters and
|
||||
hasName("FilterCollection")
|
||||
}
|
||||
|
||||
/** Gets an `Add` method. */
|
||||
Method getAddMethod() {
|
||||
result = getAMethod("Add") or
|
||||
result = getABaseType().getAMethod("Add")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Mvc.MvcOptions` class. */
|
||||
class MicrosoftAspNetCoreMvcOptions extends Class {
|
||||
MicrosoftAspNetCoreMvcOptions() {
|
||||
getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and
|
||||
hasName("MvcOptions")
|
||||
}
|
||||
|
||||
/** Gets the `Filters` property. */
|
||||
Property getFilterCollectionProperty() { result = getProperty("Filters") }
|
||||
}
|
||||
|
||||
/** The base class for controllers in MVC, i.e. `Microsoft.AspNetCore.Mvc.Controller` or `Microsoft.AspNetCore.Mvc.ControllerBase` class. */
|
||||
class MicrosoftAspNetCoreMvcControllerBaseClass extends Class {
|
||||
MicrosoftAspNetCoreMvcControllerBaseClass() {
|
||||
getNamespace() instanceof MicrosoftAspNetCoreMvcNamespace and
|
||||
(
|
||||
hasName("Controller") or
|
||||
hasName("ControllerBase")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A subtype of `Microsoft.AspNetCore.Mvc.Controller` or `Microsoft.AspNetCore.Mvc.ControllerBase`. */
|
||||
class MicrosoftAspNetCoreMvcController extends Class {
|
||||
MicrosoftAspNetCoreMvcController() {
|
||||
getABaseType*() instanceof MicrosoftAspNetCoreMvcControllerBaseClass
|
||||
}
|
||||
|
||||
/** Gets an action method for this controller. */
|
||||
Method getAnActionMethod() {
|
||||
result = getAMethod() and
|
||||
result.isPublic() and
|
||||
not result.isStatic() and
|
||||
not result.getAnAttribute() instanceof MicrosoftAspNetCoreMvcNonActionAttribute
|
||||
}
|
||||
|
||||
/** Gets a `Redirect*` method. */
|
||||
Method getARedirectMethod() {
|
||||
result = this.getAMethod() and
|
||||
result.getName().matches("Redirect%")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper` class. */
|
||||
class MicrosoftAspNetCoreMvcHtmlHelperClass extends Class {
|
||||
MicrosoftAspNetCoreMvcHtmlHelperClass() {
|
||||
getNamespace() instanceof MicrosoftAspNetCoreMvcViewFeatures and
|
||||
hasName("HtmlHelper")
|
||||
}
|
||||
|
||||
/** Gets the `Raw` method. */
|
||||
Method getRawMethod() { result = getAMethod("Raw") }
|
||||
}
|
||||
|
||||
/** A class deriving from `Microsoft.AspNetCore.Mvc.Razor.RazorPageBase`, implements Razor page in ASPNET Core. */
|
||||
class MicrosoftAspNetCoreMvcRazorPageBase extends Class {
|
||||
MicrosoftAspNetCoreMvcRazorPageBase() {
|
||||
this.getABaseType*().hasQualifiedName("Microsoft.AspNetCore.Mvc.Razor", "RazorPageBase")
|
||||
}
|
||||
|
||||
/** Gets the `WriteLiteral` method. */
|
||||
Method getWriteLiteralMethod() { result = getAMethod("WriteLiteral") }
|
||||
}
|
||||
|
||||
/** A class deriving from `Microsoft.AspNetCore.Http.HttpRequest`, implements `HttpRequest` in ASP.NET Core. */
|
||||
class MicrosoftAspNetCoreHttpHttpRequest extends Class {
|
||||
MicrosoftAspNetCoreHttpHttpRequest() {
|
||||
this.getABaseType*().hasQualifiedName("Microsoft.AspNetCore.Http", "HttpRequest")
|
||||
}
|
||||
}
|
||||
|
||||
/** A class deriving from `Microsoft.AspNetCore.Http.HttpResponse`, implements `HttpResponse` in ASP.NET Core. */
|
||||
class MicrosoftAspNetCoreHttpHttpResponse extends Class {
|
||||
MicrosoftAspNetCoreHttpHttpResponse() {
|
||||
this.getABaseType*().hasQualifiedName("Microsoft.AspNetCore.Http", "HttpResponse")
|
||||
}
|
||||
|
||||
/** Gets the `Redirect` method. */
|
||||
Method getRedirectMethod() { result = this.getAMethod("Redirect") }
|
||||
|
||||
/** Gets the `Headers` property. */
|
||||
Property getHeadersProperty() { result = this.getProperty("Headers") }
|
||||
}
|
||||
|
||||
/** An interface that is a wrapper around the collection of cookies in the response. */
|
||||
class MicrosoftAspNetCoreHttpResponseCookies extends Interface {
|
||||
MicrosoftAspNetCoreHttpResponseCookies() {
|
||||
this.hasQualifiedName("Microsoft.AspNetCore.Http.IResponseCookies")
|
||||
}
|
||||
|
||||
/** Gets the `Append` method. */
|
||||
Method getAppendMethod() { result = this.getAMethod("Append") }
|
||||
}
|
||||
|
||||
/** The class `Microsoft.AspNetCore.Http.QueryString`, holds query string in ASP.NET Core. */
|
||||
class MicrosoftAspNetCoreHttpQueryString extends Struct {
|
||||
MicrosoftAspNetCoreHttpQueryString() {
|
||||
this.hasQualifiedName("Microsoft.AspNetCore.Http", "QueryString")
|
||||
}
|
||||
}
|
||||
|
||||
/** A class or interface implementing `IQueryCollection`, holds parsed query string in ASP.NET Core. */
|
||||
class MicrosoftAspNetCoreHttpQueryCollection extends RefType {
|
||||
MicrosoftAspNetCoreHttpQueryCollection() {
|
||||
this.getABaseInterface().hasQualifiedName("Microsoft.AspNetCore.Http", "IQueryCollection")
|
||||
}
|
||||
}
|
||||
|
||||
/** The helper class `ResponseHeaders` for setting headers. */
|
||||
class MicrosoftAspNetCoreHttpResponseHeaders extends RefType {
|
||||
MicrosoftAspNetCoreHttpResponseHeaders() {
|
||||
this.hasQualifiedName("Microsoft.AspNetCore.Http.Headers", "ResponseHeaders")
|
||||
}
|
||||
|
||||
/** Gets the `Location` property. */
|
||||
Property getLocationProperty() { result = this.getProperty("Location") }
|
||||
}
|
||||
|
||||
/** The `Microsoft.AspNetCore.Http.HeaderDictionaryExtensions` class. */
|
||||
class MicrosoftAspNetCoreHttpHeaderDictionaryExtensions extends RefType {
|
||||
MicrosoftAspNetCoreHttpHeaderDictionaryExtensions() {
|
||||
this.hasQualifiedName("Microsoft.AspNetCore.Http", "HeaderDictionaryExtensions")
|
||||
}
|
||||
|
||||
/** Gets the `Append` extension method. */
|
||||
Method getAppendMethod() { result = this.getAMethod("Append") }
|
||||
|
||||
/** Gets the `AppendCommaSeparatedValues` extension method. */
|
||||
Method getAppendCommaSeparatedValuesMethod() {
|
||||
result = this.getAMethod("AppendCommaSeparatedValues")
|
||||
}
|
||||
|
||||
/** Gets the `SetCommaSeparatedValues` extension method. */
|
||||
Method getSetCommaSeparatedValuesMethod() { result = this.getAMethod("SetCommaSeparatedValues") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Microsoft.AspNetCore.Html.HtmlString` class, supposed to wrap HTML-encoded string in ASP.NET Core
|
||||
* Untrusted and unsanitized data should never flow there.
|
||||
*/
|
||||
class MicrosoftAspNetCoreHttpHtmlString extends Class {
|
||||
MicrosoftAspNetCoreHttpHtmlString() {
|
||||
this.hasQualifiedName("Microsoft.AspNetCore.Html", "HtmlString")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/** Definitions related to the namespace `System.Web.WebPages`, ASP.NET */
|
||||
import csharp
|
||||
|
||||
private import semmle.code.csharp.frameworks.system.Web
|
||||
|
||||
/** The `System.Web.WebPages` namespace. */
|
||||
class SystemWebWebPagesNamespace extends Namespace {
|
||||
SystemWebWebPagesNamespace() {
|
||||
getParentNamespace() instanceof SystemWebNamespace and
|
||||
hasName("WebPages")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `System.Web.WebPages.WebPageExecutingBase` class. */
|
||||
class SystemWebWebPagesWebPageExecutingBaseClass extends Class {
|
||||
SystemWebWebPagesWebPageExecutingBaseClass() {
|
||||
getNamespace() instanceof SystemWebWebPagesNamespace and
|
||||
hasName("WebPageExecutingBase")
|
||||
}
|
||||
}
|
||||
|
||||
/** A class that derives from `System.Web.WebPages.WebPageExecutingBase`. */
|
||||
class WebPageClass extends Class {
|
||||
WebPageClass () {
|
||||
this.getBaseClass*() instanceof SystemWebWebPagesWebPageExecutingBaseClass
|
||||
}
|
||||
|
||||
/** Gets the `WriteLiteral` method. */
|
||||
Method getWriteLiteralMethod() {
|
||||
result = getAMethod("WriteLiteral")
|
||||
}
|
||||
|
||||
/** Gets the `WriteLiteralTo` method. */
|
||||
Method getWriteLiteralToMethod() {
|
||||
result = getAMethod("WriteLiteralTo")
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ module UrlRedirect {
|
||||
import semmle.code.csharp.frameworks.system.Web
|
||||
import semmle.code.csharp.frameworks.system.web.Mvc
|
||||
import semmle.code.csharp.security.Sanitizers
|
||||
import semmle.code.csharp.frameworks.microsoft.AspNetCore
|
||||
|
||||
/**
|
||||
* A data flow source for unvalidated URL redirect vulnerabilities.
|
||||
@@ -149,4 +150,65 @@ module UrlRedirect {
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
/**
|
||||
* A URL argument to a call to `HttpResponse.Redirect()` or `Controller.Redirect()`, that is a
|
||||
* sink for URL redirects.
|
||||
*/
|
||||
class AspNetCoreRedirectSink extends Sink {
|
||||
AspNetCoreRedirectSink() {
|
||||
exists(MethodCall mc |
|
||||
mc.getTarget() = any(MicrosoftAspNetCoreHttpHttpResponse response).getRedirectMethod() or
|
||||
mc.getTarget() = any(MicrosoftAspNetCoreMvcController response).getARedirectMethod()
|
||||
|
|
||||
// Response.Redirect uses 'location' parameter
|
||||
this.getExpr() = mc.getArgumentForName("location") or
|
||||
// Redirect uses the parameter name 'url'
|
||||
this.getExpr() = mc.getArgumentForName("url") or
|
||||
// Controller.RedirectToAction*
|
||||
this.getExpr() = mc.getArgumentForName("actionName") or
|
||||
// Controller.RedirectToRoute*
|
||||
this.getExpr() = mc.getArgumentForName("routeName") or
|
||||
// Controller.RedirectToPage*
|
||||
this.getExpr() = mc.getArgumentForName("pageName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Anything that is setting "location" header in the response headers.
|
||||
*/
|
||||
class AspNetCoreLocationHeaderSink extends Sink {
|
||||
AspNetCoreLocationHeaderSink () {
|
||||
// ResponseHeaders.Location = <user-provided value>
|
||||
exists(AssignableDefinition def |
|
||||
def.getTarget() = any(MicrosoftAspNetCoreHttpResponseHeaders headers).getLocationProperty() |
|
||||
this.asExpr() = def.getSource()
|
||||
)
|
||||
or // HttpResponse.Headers.Append("location", <user-provided value>)
|
||||
exists(MethodCall mc, MicrosoftAspNetCoreHttpHeaderDictionaryExtensions ext |
|
||||
mc.getTarget() = ext.getAppendMethod() or
|
||||
mc.getTarget() = ext.getAppendCommaSeparatedValuesMethod() or
|
||||
mc.getTarget() = ext.getSetCommaSeparatedValuesMethod() |
|
||||
mc.getArgumentForName("key").getValue().toLowerCase() = "location" and
|
||||
this.getExpr() = mc.getArgument(2))
|
||||
or // HttpResponse.Headers.Add("location", <user-provided value>)
|
||||
exists(RefType cl, MicrosoftAspNetCoreHttpHttpResponse resp, PropertyAccess qualifier, MethodCall add |
|
||||
qualifier.getTarget() = resp.getHeadersProperty() and
|
||||
add.getTarget() = cl.getAMethod("Add") and
|
||||
qualifier = add.getQualifier() and
|
||||
add.getArgument(0).getValue().toLowerCase() = "location" and
|
||||
this.getExpr() = add.getArgument(1))
|
||||
or // HttpResponse.Headers["location"] = <user-provided value>
|
||||
exists(RefType cl, MicrosoftAspNetCoreHttpHttpResponse resp, IndexerAccess ci, Call cs, PropertyAccess qualifier |
|
||||
qualifier.getTarget() = resp.getHeadersProperty() and
|
||||
ci.getTarget() = cl.getAnIndexer() and
|
||||
qualifier = ci.getQualifier() and
|
||||
cs.getTarget() = cl.getAnIndexer().getSetter() and
|
||||
cs.getArgument(0).getValue().toLowerCase() = "location" and
|
||||
this.asExpr() = cs.getArgument(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@ import csharp
|
||||
|
||||
module XSS {
|
||||
import semmle.code.csharp.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.frameworks.microsoft.AspNetCore
|
||||
import semmle.code.csharp.frameworks.system.Net
|
||||
import semmle.code.csharp.frameworks.system.Web
|
||||
import semmle.code.csharp.frameworks.system.web.Mvc
|
||||
import semmle.code.csharp.frameworks.system.web.WebPages
|
||||
import semmle.code.csharp.frameworks.system.web.UI
|
||||
import semmle.code.csharp.frameworks.system.web.ui.WebControls
|
||||
import semmle.code.csharp.frameworks.system.windows.Forms
|
||||
@@ -514,6 +516,75 @@ module XSS {
|
||||
this.getExpr() = any(ObjectCreation oc | oc.getTarget().getDeclaringType().hasQualifiedName("System.Net.Http", "StringContent")).getArgumentForName("content")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is used as an argument to `Page.WriteLiteral`, typically in
|
||||
* a `.cshtml` file.
|
||||
*/
|
||||
class WebPageWriteLiteralSink extends Sink, HtmlSink {
|
||||
WebPageWriteLiteralSink() {
|
||||
this.getExpr() = any(WebPageClass h).getWriteLiteralMethod().getACall().getAnArgument()
|
||||
}
|
||||
|
||||
override string explanation() {
|
||||
result = "System.Web.WebPages.WebPage.WriteLiteral() method"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is used as an argument to `Page.WriteLiteralTo`, typically in
|
||||
* a `.cshtml` file.
|
||||
*/
|
||||
class WebPageWriteLiteralToSink extends Sink, HtmlSink {
|
||||
WebPageWriteLiteralToSink() {
|
||||
this.getExpr() = any(WebPageClass h).getWriteLiteralToMethod().getACall().getAnArgument()
|
||||
}
|
||||
|
||||
override string explanation() {
|
||||
result = "System.Web.WebPages.WebPage.WriteLiteralTo() method"
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AspNetCoreSink extends Sink, HtmlSink { }
|
||||
|
||||
/**
|
||||
* An expression that is used as an argument to `HtmlHelper.Raw`, typically in
|
||||
* a `.cshtml` file.
|
||||
*/
|
||||
class MicrosoftAspNetCoreMvcHtmlHelperRawSink extends AspNetCoreSink {
|
||||
MicrosoftAspNetCoreMvcHtmlHelperRawSink() {
|
||||
this.getExpr() = any(MicrosoftAspNetCoreMvcHtmlHelperClass h).getRawMethod().getACall().getAnArgument()
|
||||
}
|
||||
|
||||
override string explanation() {
|
||||
result = "Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper.Raw() method"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is used as an argument to `Page.WriteLiteral` in ASP.NET 6.0 razor page, typically in
|
||||
* a `.cshtml` file.
|
||||
*/
|
||||
class MicrosoftAspNetRazorPageWriteLiteralSink extends AspNetCoreSink {
|
||||
MicrosoftAspNetRazorPageWriteLiteralSink() {
|
||||
this.getExpr() = any(MicrosoftAspNetCoreMvcRazorPageBase h).getWriteLiteralMethod().getACall().getAnArgument()
|
||||
}
|
||||
|
||||
override string explanation() {
|
||||
result = "Microsoft.AspNetCore.Mvc.Razor.RazorPageBase.WriteLiteral() method"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HtmlString that may be rendered as is need to have sanitized value
|
||||
*/
|
||||
class MicrosoftAspNetHtmlStringSink extends AspNetCoreSink {
|
||||
MicrosoftAspNetHtmlStringSink() {
|
||||
exists (ObjectCreation c, MicrosoftAspNetCoreHttpHtmlString s |
|
||||
c.getTarget() = s.getAConstructor() and
|
||||
this.asExpr() = c.getAnArgument())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Type getMemberType(Member m) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// semmle-extractor-options: /r:${testdir}/../../../resources/assemblies/System.Data.dll /r:${testdir}/../../../resources/assemblies/System.Web.dll /r:${testdir}/../../../resources/assemblies/System.Web.Mvc.dll /r:System.ComponentModel.Primitives.dll /r:System.Collections.Specialized.dll /r:${testdir}/../../../resources/assemblies/System.Net.Http.dll
|
||||
// semmle-extractor-options: /r:${testdir}/../../../../resources/assemblies/System.Data.dll /r:${testdir}/../../../../resources/assemblies/System.Web.dll /r:${testdir}/../../../../resources/assemblies/System.Web.Mvc.dll /r:System.ComponentModel.Primitives.dll /r:System.Collections.Specialized.dll /r:${testdir}/../../../../resources/assemblies/System.Net.Http.dll
|
||||
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
@@ -0,0 +1,10 @@
|
||||
| XSSAspNet.cs:27:30:27:34 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteral() method. | XSSAspNet.cs:20:25:20:43 | access to property QueryString | User-provided value |
|
||||
| XSSAspNet.cs:37:40:37:44 | access to local variable sayHi | $@ flows to here and is written to HTML or JavaScript: System.Web.WebPages.WebPage.WriteLiteralTo() method. | XSSAspNet.cs:20:25:20:43 | access to property QueryString | User-provided value |
|
||||
| XSSAspNet.cs:44:28:44:55 | access to indexer | $@ flows to here and is written to HTML or JavaScript. | XSSAspNet.cs:44:28:44:46 | access to property QueryString | User-provided value |
|
||||
| XSSAspNetCore.cs:21:52:21:76 | call to operator implicit conversion | $@ flows to here and is written to HTML or JavaScript. | XSSAspNetCore.cs:21:52:21:64 | access to property Query | User-provided value |
|
||||
| XSSAspNetCore.cs:44:51:44:53 | access to parameter foo | $@ flows to here and is written to HTML or JavaScript. | XSSAspNetCore.cs:40:56:40:58 | foo | User-provided value |
|
||||
| XSSAspNetCore.cs:51:43:51:67 | access to property Value | $@ flows to here and is written to HTML or JavaScript. | XSSAspNetCore.cs:51:43:51:67 | access to property Value | User-provided value |
|
||||
| XSSAspNetCore.cs:58:43:58:73 | call to method ToString | $@ flows to here and is written to HTML or JavaScript. | XSSAspNetCore.cs:58:43:58:55 | access to property Query | User-provided value |
|
||||
| XSSAspNetCore.cs:61:44:61:66 | access to indexer | $@ flows to here and is written to HTML or JavaScript. | XSSAspNetCore.cs:61:44:61:56 | access to property Query | User-provided value |
|
||||
| XSSAspNetCore.cs:69:43:69:61 | access to property ContentType | $@ flows to here and is written to HTML or JavaScript. | XSSAspNetCore.cs:69:43:69:61 | access to property ContentType | User-provided value |
|
||||
| XSSAspNetCore.cs:72:51:72:72 | call to operator implicit conversion | $@ flows to here and is written to HTML or JavaScript. | XSSAspNetCore.cs:72:51:72:65 | access to property Headers | User-provided value |
|
||||
@@ -0,0 +1 @@
|
||||
Security Features/CWE-079/XSS.ql
|
||||
@@ -0,0 +1,51 @@
|
||||
// semmle-extractor-options: /r:System.Dynamic.Runtime.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Expressions.dll
|
||||
namespace ASP
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Web;
|
||||
using System.Web.WebPages;
|
||||
|
||||
public class _Page_Views_Home_Contact_cshtml : System.Web.Mvc.WebViewPage<dynamic>
|
||||
{
|
||||
public _Page_Views_Home_Contact_cshtml()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
Layout = "~/_SiteLayout.cshtml";
|
||||
Page.Title = "Contact";
|
||||
var sayHi = Request.QueryString["sayHi"];
|
||||
if (sayHi.IsEmpty())
|
||||
{
|
||||
WriteLiteral("<script>alert(\"XSS via WriteLiteral\")</script>"); // GOOD: hard-coded, not user input
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLiteral(sayHi); // BAD: user input flows to HTML unencoded
|
||||
WriteLiteral(HttpUtility.HtmlEncode(sayHi)); // Good: user input is encoded before it flows to HTML
|
||||
}
|
||||
|
||||
if (sayHi.IsEmpty())
|
||||
{
|
||||
WriteLiteralTo(Output, "<script > alert(\"XSS via WriteLiteralTo\")</script>"); // GOOD: hard-coded, not user input
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLiteralTo(Output, sayHi); // BAD: user input flows to HTML unencoded
|
||||
WriteLiteralTo(Output, Html.Encode(sayHi)); // Good: user input is encoded before it flows to HTML
|
||||
}
|
||||
|
||||
BeginContext("~/Views/Home/Contact.cshtml", 288, 32, false);
|
||||
|
||||
Write(Html.Raw("<script>alert(\"XSS via Html.Raw()\")</script>")); // GOOD: hard-coded, not user input
|
||||
Write(Html.Raw(Request.QueryString["sayHi"])); // BAD: user input flows to HTML unencoded
|
||||
Write(Html.Raw(HttpContext.Current.Server.HtmlEncode(Request.QueryString["sayHi"]))); // Good: user input is encoded before it flows to HTML
|
||||
EndContext("~/Views/Home/Contact.cshtml", 288, 32, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// source-extractor-options: /r:${testdir}/../../../../../packages/Microsoft.AspNet.WebPages.3.2.3/lib/net45/System.Web.WebPages.dll /r:${testdir}/../../../../../packages/Microsoft.AspNet.Mvc.5.2.3/lib/net45/System.Web.Mvc.dll /r:System.Dynamic.Runtime.dll /r:System.Runtime.Extensions.dll /r:System.Linq.Expressions.dll /r:System.Web.dll /r:C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.Web.dll /r:System.Collections.Specialized.dll
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Testing.Controllers
|
||||
{
|
||||
public class HomeViewModel
|
||||
{
|
||||
public string Message { get; set; }
|
||||
|
||||
public HtmlString Description { get; set; }
|
||||
}
|
||||
|
||||
public class TestController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
// BAD: flow of content type to.
|
||||
var v = new ViewResult();
|
||||
v.ViewData["BadData"] = new HtmlString(Request.Query["Bad data"]);
|
||||
|
||||
StringValues vOut;
|
||||
Request.Query.TryGetValue("Foo", out vOut);
|
||||
|
||||
// BAD: via Enumerable. (false negative)
|
||||
v.ViewData["FooFirst"] = new HtmlString(vOut.First());
|
||||
|
||||
// BAD: via toArray. (false negative)
|
||||
v.ViewData["FooArray0"] = new HtmlString(vOut.ToArray()[0]);
|
||||
|
||||
// BAD: via implicit conversion operator. (false negative)
|
||||
v.ViewData["FooImplicit"] = new HtmlString(vOut);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
[HttpPost("Test")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public IActionResult Submit([FromQuery] string foo)
|
||||
{
|
||||
var view = new ViewResult();
|
||||
//BAD: flow of submitted value to view in HtmlString.
|
||||
view.ViewData["FOO"] = new HtmlString(foo);
|
||||
return view;
|
||||
}
|
||||
|
||||
public IActionResult IndexToModel()
|
||||
{
|
||||
//BAD: flow of submitted value to view in HtmlString.
|
||||
HtmlString v = new HtmlString(Request.QueryString.Value);
|
||||
return View(new HomeViewModel() { Message = "Message from Index", Description = v });
|
||||
}
|
||||
|
||||
public IActionResult About()
|
||||
{
|
||||
//BAD: flow of submitted value to view in HtmlString.
|
||||
HtmlString v = new HtmlString(Request.Query["Foo"].ToString());
|
||||
|
||||
//BAD: flow of submitted value to view in HtmlString.
|
||||
HtmlString v1 = new HtmlString(Request.Query["Foo"][0]);
|
||||
|
||||
return View(new HomeViewModel() { Message = "Message from About", Description = v });
|
||||
}
|
||||
|
||||
public IActionResult Contact()
|
||||
{
|
||||
//BAD: flow of user content type to view in HtmlString.
|
||||
HtmlString v = new HtmlString(Request.ContentType);
|
||||
|
||||
//BAD: flow of headers to view in HtmlString.
|
||||
HtmlString v1 = new HtmlString(value: Request.Headers["Foo"]);
|
||||
|
||||
return View(new HomeViewModel() { Message = "Message from Contact", Description = v });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initial-extractor-options: /r:netstandard.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.Core.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.Core.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Antiforgery.1.1.2/lib/net451/Microsoft.AspNetCore.Antiforgery.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.ViewFeatures.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.ViewFeatures.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Mvc.Abstractions.1.1.3/lib/net451/Microsoft.AspNetCore.Mvc.Abstractions.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Http.Abstractions.1.1.2\lib\net451\Microsoft.AspNetCore.Http.Abstractions.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Html.Abstractions.1.1.2/lib/netstandard1.0/Microsoft.AspNetCore.Html.Abstractions.dll /r:${testdir}/../../../../../packages/Microsoft.AspNetCore.Http.Features.1.1.2\lib\net451\Microsoft.AspNetCore.Http.Features.dll /r:${testdir}/../../../../../packages\Microsoft.Extensions.Primitives.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll /r:System.Linq.dll /r:System.Linq.Expressions.dll /r:System.Linq.Queryable.dll
|
||||
@@ -0,0 +1,236 @@
|
||||
namespace Microsoft
|
||||
{
|
||||
namespace AspNetCore
|
||||
{
|
||||
namespace Html
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Html.HtmlString` in `Microsoft.AspNetCore.Html.Abstractions, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class HtmlString : Microsoft.AspNetCore.Html.IHtmlContent
|
||||
{
|
||||
public HtmlString(string value) => throw null;
|
||||
public override string ToString() => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Html.IHtmlContent` in `Microsoft.AspNetCore.Html.Abstractions, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IHtmlContent
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
namespace Http
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Http.HttpRequest` in `Microsoft.AspNetCore.Http.Abstractions, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class HttpRequest
|
||||
{
|
||||
public abstract Microsoft.AspNetCore.Http.IHeaderDictionary Headers { get; }
|
||||
public abstract Microsoft.AspNetCore.Http.IQueryCollection Query { get; set; }
|
||||
public abstract Microsoft.AspNetCore.Http.QueryString QueryString { get; set; }
|
||||
public abstract string ContentType { get; set; }
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Http.IHeaderDictionary` in `Microsoft.AspNetCore.Http.Features, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IHeaderDictionary : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues>>, System.Collections.Generic.IDictionary<string, Microsoft.Extensions.Primitives.StringValues>, System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues>>
|
||||
{
|
||||
Microsoft.Extensions.Primitives.StringValues this[string key] { get; set; }
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Http.IQueryCollection` in `Microsoft.AspNetCore.Http.Features, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IQueryCollection : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues>>
|
||||
{
|
||||
Microsoft.Extensions.Primitives.StringValues this[string key] { get; }
|
||||
bool TryGetValue(string key, out Microsoft.Extensions.Primitives.StringValues value);
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Http.QueryString` in `Microsoft.AspNetCore.Http.Abstractions, Version=1.1.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public struct QueryString : System.IEquatable<Microsoft.AspNetCore.Http.QueryString>
|
||||
{
|
||||
public bool Equals(Microsoft.AspNetCore.Http.QueryString other) => throw null;
|
||||
public override bool Equals(object obj) => throw null;
|
||||
public override int GetHashCode() => throw null;
|
||||
public override string ToString() => throw null;
|
||||
public string Value { get => throw null; }
|
||||
}
|
||||
|
||||
}
|
||||
namespace Mvc
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ActionResult` in `Microsoft.AspNetCore.Mvc.Core, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class ActionResult : Microsoft.AspNetCore.Mvc.IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ControllerBase` in `Microsoft.AspNetCore.Mvc.Core, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class ControllerBase
|
||||
{
|
||||
public Microsoft.AspNetCore.Http.HttpRequest Request { get => throw null; }
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Controller` in `Microsoft.AspNetCore.Mvc.ViewFeatures, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class Controller : Microsoft.AspNetCore.Mvc.ControllerBase, System.IDisposable, Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata, Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter, Microsoft.AspNetCore.Mvc.Filters.IActionFilter
|
||||
{
|
||||
public virtual Microsoft.AspNetCore.Mvc.ViewResult View(object model) => throw null;
|
||||
public void Dispose() => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.FromQueryAttribute` in `Microsoft.AspNetCore.Mvc.Core, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class FromQueryAttribute : System.Attribute, Microsoft.AspNetCore.Mvc.ModelBinding.IModelNameProvider, Microsoft.AspNetCore.Mvc.ModelBinding.IBindingSourceMetadata
|
||||
{
|
||||
public FromQueryAttribute() => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.HttpPostAttribute` in `Microsoft.AspNetCore.Mvc.Core, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class HttpPostAttribute : Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute
|
||||
{
|
||||
public HttpPostAttribute() => throw null;
|
||||
public HttpPostAttribute(string template) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.IActionResult` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ValidateAntiForgeryTokenAttribute` in `Microsoft.AspNetCore.Mvc.ViewFeatures, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class ValidateAntiForgeryTokenAttribute : System.Attribute, Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter, Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata, Microsoft.AspNetCore.Mvc.Filters.IFilterFactory
|
||||
{
|
||||
public ValidateAntiForgeryTokenAttribute() => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ViewResult` in `Microsoft.AspNetCore.Mvc.ViewFeatures, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class ViewResult : Microsoft.AspNetCore.Mvc.ActionResult
|
||||
{
|
||||
public Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary ViewData { get => throw null; set => throw null; }
|
||||
public ViewResult() => throw null;
|
||||
}
|
||||
|
||||
namespace Filters
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Filters.IActionFilter` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IActionFilter : Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Filters.IAsyncActionFilter` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IAsyncActionFilter : Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Filters.IFilterFactory` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IFilterFactory : Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IFilterMetadata
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Filters.IOrderedFilter` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IOrderedFilter : Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
namespace ModelBinding
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ModelBinding.IBindingSourceMetadata` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IBindingSourceMetadata
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ModelBinding.IModelNameProvider` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IModelNameProvider
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
namespace Routing
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute` in `Microsoft.AspNetCore.Mvc.Core, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class HttpMethodAttribute : System.Attribute, Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider, Microsoft.AspNetCore.Mvc.Routing.IActionHttpMethodProvider
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Routing.IActionHttpMethodProvider` in `Microsoft.AspNetCore.Mvc.Core, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IActionHttpMethodProvider
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider` in `Microsoft.AspNetCore.Mvc.Core, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IRouteTemplateProvider
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
namespace ViewFeatures
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary` in `Microsoft.AspNetCore.Mvc.ViewFeatures, Version=1.1.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class ViewDataDictionary : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>, System.Collections.Generic.IDictionary<string, object>, System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>
|
||||
{
|
||||
System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object>>.GetEnumerator() => throw null;
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
|
||||
public System.Collections.Generic.ICollection<object> Values { get => throw null; }
|
||||
public System.Collections.Generic.ICollection<string> Keys { get => throw null; }
|
||||
public bool Contains(System.Collections.Generic.KeyValuePair<string, object> item) => throw null;
|
||||
public bool ContainsKey(string key) => throw null;
|
||||
public bool IsReadOnly { get => throw null; }
|
||||
public bool Remove(System.Collections.Generic.KeyValuePair<string, object> item) => throw null;
|
||||
public bool Remove(string key) => throw null;
|
||||
public bool TryGetValue(string key, out object value) => throw null;
|
||||
public int Count { get => throw null; }
|
||||
public object this[string index] { get => throw null; set => throw null; }
|
||||
public void Add(System.Collections.Generic.KeyValuePair<string, object> item) => throw null;
|
||||
public void Add(string key, object value) => throw null;
|
||||
public void Clear() => throw null;
|
||||
public void CopyTo(System.Collections.Generic.KeyValuePair<string, object>[] array, int arrayIndex) => throw null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Extensions
|
||||
{
|
||||
namespace Primitives
|
||||
{
|
||||
// Generated from `Microsoft.Extensions.Primitives.StringValues` in `Microsoft.Extensions.Primitives, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public struct StringValues : System.IEquatable<string[]>, System.IEquatable<string>, System.IEquatable<Microsoft.Extensions.Primitives.StringValues>, System.Collections.IEnumerable, System.Collections.Generic.IReadOnlyList<string>, System.Collections.Generic.IReadOnlyCollection<string>,
|
||||
System.Collections.Generic.IList<string>, System.Collections.Generic.IEnumerable<string>, System.Collections.Generic.ICollection<string>
|
||||
{
|
||||
System.Collections.Generic.IEnumerator<string> System.Collections.Generic.IEnumerable<string>.GetEnumerator() => throw null;
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
|
||||
bool System.Collections.Generic.ICollection<string>.Contains(string item) => throw null;
|
||||
bool System.Collections.Generic.ICollection<string>.IsReadOnly { get => throw null; }
|
||||
bool System.Collections.Generic.ICollection<string>.Remove(string item) => throw null;
|
||||
int System.Collections.Generic.IList<string>.IndexOf(string item) => throw null;
|
||||
public bool Equals(Microsoft.Extensions.Primitives.StringValues other) => throw null;
|
||||
public bool Equals(string other) => throw null;
|
||||
public bool Equals(string[] other) => throw null;
|
||||
public int Count { get => throw null; }
|
||||
public override bool Equals(object obj) => throw null;
|
||||
public override int GetHashCode() => throw null;
|
||||
public override string ToString() => throw null;
|
||||
public static implicit operator string(Microsoft.Extensions.Primitives.StringValues values) => throw null;
|
||||
public string this[int index] { get => throw null; set => throw null; }
|
||||
public string[] ToArray() => throw null;
|
||||
void System.Collections.Generic.ICollection<string>.Add(string item) => throw null;
|
||||
void System.Collections.Generic.ICollection<string>.Clear() => throw null;
|
||||
void System.Collections.Generic.ICollection<string>.CopyTo(string[] array, int arrayIndex) => throw null;
|
||||
void System.Collections.Generic.IList<string>.Insert(int index, string item) => throw null;
|
||||
void System.Collections.Generic.IList<string>.RemoveAt(int index) => throw null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace System
|
||||
{
|
||||
namespace Linq
|
||||
{
|
||||
// Generated from `System.Linq.Enumerable` in `System.Linq, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
static public class Enumerable
|
||||
{
|
||||
public static TSource First<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) => throw null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
namespace System
|
||||
{
|
||||
namespace Collections
|
||||
{
|
||||
namespace Specialized
|
||||
{
|
||||
// Generated from `System.Collections.Specialized.NameObjectCollectionBase` in `System.Collections.Specialized, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
abstract public class NameObjectCollectionBase : System.Runtime.Serialization.ISerializable, System.Runtime.Serialization.IDeserializationCallback, System.Collections.IEnumerable, System.Collections.ICollection
|
||||
{
|
||||
bool System.Collections.ICollection.IsSynchronized { get => throw null; }
|
||||
object System.Collections.ICollection.SyncRoot { get => throw null; }
|
||||
public virtual System.Collections.IEnumerator GetEnumerator() => throw null;
|
||||
public virtual int Count { get => throw null; }
|
||||
public virtual void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) => throw null;
|
||||
public virtual void OnDeserialization(object sender) => throw null;
|
||||
void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `System.Collections.Specialized.NameValueCollection` in `System.Collections.Specialized, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
public class NameValueCollection : System.Collections.Specialized.NameObjectCollectionBase
|
||||
{
|
||||
public string this[string name] { get => throw null; set => throw null; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
namespace IO
|
||||
{
|
||||
// Generated from `System.IO.TextWriter` in `System.Runtime.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
abstract public class TextWriter : System.MarshalByRefObject, System.IDisposable
|
||||
{
|
||||
public void Dispose() => throw null;
|
||||
}
|
||||
|
||||
}
|
||||
namespace Web
|
||||
{
|
||||
// Generated from `System.Web.HttpContext` in `System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
public class HttpContext : System.Web.IPrincipalContainer
|
||||
{
|
||||
public System.Web.HttpServerUtility Server { get => throw null; }
|
||||
public static System.Web.HttpContext Current { get => throw null; set => throw null; }
|
||||
}
|
||||
|
||||
// Generated from `System.Web.HttpRequestBase` in `System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
abstract public class HttpRequestBase
|
||||
{
|
||||
public virtual System.Collections.Specialized.NameValueCollection QueryString { get => throw null; }
|
||||
}
|
||||
|
||||
// Generated from `System.Web.HttpServerUtility` in `System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
public class HttpServerUtility
|
||||
{
|
||||
public string HtmlEncode(string s) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `System.Web.HttpUtility` in `System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
public class HttpUtility
|
||||
{
|
||||
public static string HtmlEncode(string s) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `System.Web.IHtmlString` in `System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
public interface IHtmlString
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `System.Web.IPrincipalContainer` in `System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
interface IPrincipalContainer
|
||||
{
|
||||
}
|
||||
|
||||
namespace Mvc
|
||||
{
|
||||
// Generated from `System.Web.Mvc.HtmlHelper<>` in `System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
public class HtmlHelper<TModel> : System.Web.Mvc.HtmlHelper
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `System.Web.Mvc.HtmlHelper` in `System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
public class HtmlHelper
|
||||
{
|
||||
public System.Web.IHtmlString Raw(string value) => throw null;
|
||||
public string Encode(string value) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `System.Web.Mvc.IViewDataContainer` in `System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
public interface IViewDataContainer
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `System.Web.Mvc.IViewStartPageChild` in `System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
interface IViewStartPageChild
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `System.Web.Mvc.WebViewPage<>` in `System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
abstract public class WebViewPage<TModel> : System.Web.Mvc.WebViewPage
|
||||
{
|
||||
public System.Web.Mvc.HtmlHelper<TModel> Html { get => throw null; set => throw null; }
|
||||
}
|
||||
|
||||
// Generated from `System.Web.Mvc.WebViewPage` in `System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
abstract public class WebViewPage : System.Web.WebPages.WebPageBase, System.Web.Mvc.IViewStartPageChild, System.Web.Mvc.IViewDataContainer
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
namespace WebPages
|
||||
{
|
||||
// Generated from `System.Web.WebPages.ITemplateFile` in `System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
public interface ITemplateFile
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `System.Web.WebPages.StringExtensions` in `System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
static public class StringExtensions
|
||||
{
|
||||
public static bool IsEmpty(this string value) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `System.Web.WebPages.WebPageBase` in `System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
abstract public class WebPageBase : System.Web.WebPages.WebPageRenderingBase
|
||||
{
|
||||
public System.IO.TextWriter Output { get => throw null; }
|
||||
public override dynamic Page { get => throw null; }
|
||||
public override string Layout { get => throw null; set => throw null; }
|
||||
public override void Write(object value) => throw null;
|
||||
public override void WriteLiteral(object value) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `System.Web.WebPages.WebPageExecutingBase` in `System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
abstract public class WebPageExecutingBase
|
||||
{
|
||||
protected void BeginContext(string virtualPath, int startPosition, int length, bool isLiteral) => throw null;
|
||||
protected void EndContext(string virtualPath, int startPosition, int length, bool isLiteral) => throw null;
|
||||
public abstract void Execute();
|
||||
public abstract void Write(object value);
|
||||
public abstract void WriteLiteral(object value);
|
||||
public static void WriteLiteralTo(System.IO.TextWriter writer, object content) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `System.Web.WebPages.WebPageRenderingBase` in `System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`
|
||||
abstract public class WebPageRenderingBase : System.Web.WebPages.WebPageExecutingBase, System.Web.WebPages.ITemplateFile
|
||||
{
|
||||
public abstract dynamic Page { get; }
|
||||
public abstract string Layout { get; set; }
|
||||
public virtual System.Web.HttpRequestBase Request { get => throw null; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
| UrlRedirect.cs:14:31:14:61 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:14:31:14:53 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:39:44:39:74 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:39:44:39:66 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:40:47:40:77 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:40:47:40:69 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:49:29:49:31 | access to local variable url | Untrusted URL redirection due to $@. | UrlRedirect.cs:24:22:24:44 | access to property QueryString | user-provided value |
|
||||
@@ -1,4 +1,4 @@
|
||||
// semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Web.cs /r:System.Collections.Specialized.dll
|
||||
// semmle-extractor-options: ${testdir}/../../../../resources/stubs/System.Web.cs /r:System.Collections.Specialized.dll
|
||||
|
||||
using System;
|
||||
using System.Web;
|
||||
@@ -0,0 +1,14 @@
|
||||
| UrlRedirect.cs:14:31:14:61 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:14:31:14:53 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:39:44:39:74 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:39:44:39:66 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:40:47:40:77 | access to indexer | Untrusted URL redirection due to $@. | UrlRedirect.cs:40:47:40:69 | access to property QueryString | user-provided value |
|
||||
| UrlRedirect.cs:49:29:49:31 | access to local variable url | Untrusted URL redirection due to $@. | UrlRedirect.cs:24:22:24:44 | access to property QueryString | user-provided value |
|
||||
| UrlRedirectCore.cs:18:22:18:26 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:15:44:15:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:21:44:21:48 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:15:44:15:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:27:46:27:50 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:15:44:15:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:33:66:33:70 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:15:44:15:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:36:49:36:53 | call to operator implicit conversion | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:15:44:15:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:39:69:39:73 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:15:44:15:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:42:39:42:53 | ... + ... | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:15:44:15:48 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:50:28:50:32 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:47:51:47:55 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:55:32:55:45 | object creation of type Uri | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:47:51:47:55 | value | user-provided value |
|
||||
| UrlRedirectCore.cs:58:31:58:35 | access to parameter value | Untrusted URL redirection due to $@. | UrlRedirectCore.cs:47:51:47:55 | value | user-provided value |
|
||||
@@ -0,0 +1,67 @@
|
||||
// semmle-extractor-options: /r:System.Private.Uri.dll
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Headers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Testing.Controllers
|
||||
{
|
||||
public class SomeController : ControllerBase
|
||||
{
|
||||
private static string SomeValue = "HeaderValue";
|
||||
|
||||
[HttpPost]
|
||||
public void Post([FromBody] string value)
|
||||
{
|
||||
// BAD: straight up controller redirect
|
||||
Redirect(value);
|
||||
|
||||
// BAD: Setting response headers collection, location = redirect
|
||||
Response.Headers["location"] = value;
|
||||
|
||||
// GOOD: Setting response header to a constant value
|
||||
Response.Headers["location"] = SomeValue;
|
||||
|
||||
// BAD: Setting response headers collection, location = redirect via add method
|
||||
Response.Headers.Add("location", value);
|
||||
|
||||
// GOOD: Setting response header to a constant value
|
||||
Response.Headers.Add("location", "foo");
|
||||
|
||||
// BAD: redirect via location
|
||||
Response.Headers.SetCommaSeparatedValues("location", value);
|
||||
|
||||
// BAD = redirect via setting location value from tainted source
|
||||
Response.Headers.Append("location", value);
|
||||
|
||||
// BAD: redirect via setting location header from comma-separated values
|
||||
Response.Headers.AppendCommaSeparatedValues("location", value);
|
||||
|
||||
// BAD: tainted redirect to Action
|
||||
RedirectToActionPermanent("Error" + value);
|
||||
}
|
||||
|
||||
// PUT: api/Some/5
|
||||
[HttpPut("{id}")]
|
||||
public void Put(int id, [FromBody] string value)
|
||||
{
|
||||
|
||||
RedirectToPage(value);
|
||||
|
||||
var headers = new ResponseHeaders(Response.Headers);
|
||||
|
||||
// BAD: redirect via header helper class
|
||||
headers.Location = new Uri(value);
|
||||
|
||||
// BAD: response redirect
|
||||
Response.Redirect(value);
|
||||
|
||||
// GOOD: whitelisted redirect
|
||||
if(Url.IsLocalUrl(value))
|
||||
Redirect(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// original-extractor-options: /r:netstandard.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Mvc.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Mvc.Core.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Core.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Http.Extensions.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Extensions.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Http.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Abstractions.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Mvc.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.Abstractions.dll /r:${testdir}/../../../../../packages\Microsoft.AspNetCore.Http.Features.2.1.0\lib\netstandard2.0\Microsoft.AspNetCore.Http.Features.dll /r:${testdir}/../../../../../packages\Microsoft.Extensions.Primitives.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll /r:System.Private.Uri.dll
|
||||
@@ -0,0 +1,182 @@
|
||||
namespace Microsoft
|
||||
{
|
||||
namespace AspNetCore
|
||||
{
|
||||
namespace Http
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Http.HeaderDictionaryExtensions` in `Microsoft.AspNetCore.Http.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
static public class HeaderDictionaryExtensions
|
||||
{
|
||||
public static void Append(this Microsoft.AspNetCore.Http.IHeaderDictionary headers, string key, Microsoft.Extensions.Primitives.StringValues value) => throw null;
|
||||
public static void AppendCommaSeparatedValues(this Microsoft.AspNetCore.Http.IHeaderDictionary headers, string key, params string[] values) => throw null;
|
||||
public static void SetCommaSeparatedValues(this Microsoft.AspNetCore.Http.IHeaderDictionary headers, string key, params string[] values) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Http.HttpResponse` in `Microsoft.AspNetCore.Http.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class HttpResponse
|
||||
{
|
||||
public abstract Microsoft.AspNetCore.Http.IHeaderDictionary Headers { get; }
|
||||
public virtual void Redirect(string location) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Http.IHeaderDictionary` in `Microsoft.AspNetCore.Http.Features, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IHeaderDictionary : System.Collections.IEnumerable, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,Microsoft.Extensions.Primitives.StringValues>>, System.Collections.Generic.IDictionary<string,Microsoft.Extensions.Primitives.StringValues>, System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string,Microsoft.Extensions.Primitives.StringValues>>
|
||||
{
|
||||
Microsoft.Extensions.Primitives.StringValues this[string key] { get; set; }
|
||||
}
|
||||
|
||||
namespace Headers
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Http.Headers.ResponseHeaders` in `Microsoft.AspNetCore.Http.Extensions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class ResponseHeaders
|
||||
{
|
||||
public ResponseHeaders(Microsoft.AspNetCore.Http.IHeaderDictionary headers) => throw null;
|
||||
public System.Uri Location { get => throw null; set => throw null; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
namespace Mvc
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ActionResult` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class ActionResult : Microsoft.AspNetCore.Mvc.IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ControllerBase` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class ControllerBase
|
||||
{
|
||||
public Microsoft.AspNetCore.Http.HttpResponse Response { get => throw null; }
|
||||
public Microsoft.AspNetCore.Mvc.IUrlHelper Url { get => throw null; set => throw null; }
|
||||
public virtual Microsoft.AspNetCore.Mvc.RedirectResult Redirect(string url) => throw null;
|
||||
public virtual Microsoft.AspNetCore.Mvc.RedirectToActionResult RedirectToActionPermanent(string actionName) => throw null;
|
||||
public virtual Microsoft.AspNetCore.Mvc.RedirectToPageResult RedirectToPage(string pageName) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.FromBodyAttribute` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class FromBodyAttribute : System.Attribute, Microsoft.AspNetCore.Mvc.ModelBinding.IBindingSourceMetadata
|
||||
{
|
||||
public FromBodyAttribute() => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.HttpPostAttribute` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class HttpPostAttribute : Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute
|
||||
{
|
||||
public HttpPostAttribute() => throw null;
|
||||
public HttpPostAttribute(string template) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.HttpPutAttribute` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class HttpPutAttribute : Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute
|
||||
{
|
||||
public HttpPutAttribute() => throw null;
|
||||
public HttpPutAttribute(string template) => throw null;
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.IActionResult` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.IUrlHelper` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IUrlHelper
|
||||
{
|
||||
bool IsLocalUrl(string url);
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.RedirectResult` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class RedirectResult : Microsoft.AspNetCore.Mvc.ActionResult, Microsoft.AspNetCore.Mvc.ViewFeatures.IKeepTempDataResult, Microsoft.AspNetCore.Mvc.IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.RedirectToActionResult` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class RedirectToActionResult : Microsoft.AspNetCore.Mvc.ActionResult, Microsoft.AspNetCore.Mvc.ViewFeatures.IKeepTempDataResult, Microsoft.AspNetCore.Mvc.IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.RedirectToPageResult` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public class RedirectToPageResult : Microsoft.AspNetCore.Mvc.ActionResult, Microsoft.AspNetCore.Mvc.ViewFeatures.IKeepTempDataResult, Microsoft.AspNetCore.Mvc.IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
namespace ModelBinding
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ModelBinding.IBindingSourceMetadata` in `Microsoft.AspNetCore.Mvc.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IBindingSourceMetadata
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
namespace Routing
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
abstract public class HttpMethodAttribute : System.Attribute, Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider, Microsoft.AspNetCore.Mvc.Routing.IActionHttpMethodProvider
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Routing.IActionHttpMethodProvider` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IActionHttpMethodProvider
|
||||
{
|
||||
}
|
||||
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.Routing.IRouteTemplateProvider` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IRouteTemplateProvider
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
namespace ViewFeatures
|
||||
{
|
||||
// Generated from `Microsoft.AspNetCore.Mvc.ViewFeatures.IKeepTempDataResult` in `Microsoft.AspNetCore.Mvc.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public interface IKeepTempDataResult : Microsoft.AspNetCore.Mvc.IActionResult
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Extensions
|
||||
{
|
||||
namespace Primitives
|
||||
{
|
||||
// Generated from `Microsoft.Extensions.Primitives.StringValues` in `Microsoft.Extensions.Primitives, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60`
|
||||
public struct StringValues : System.IEquatable<string[]>, System.IEquatable<string>, System.IEquatable<Microsoft.Extensions.Primitives.StringValues>, System.Collections.IEnumerable, System.Collections.Generic.IReadOnlyList<string>, System.Collections.Generic.IReadOnlyCollection<string>, System.Collections.Generic.IList<string>, System.Collections.Generic.IEnumerable<string>, System.Collections.Generic.ICollection<string>
|
||||
{
|
||||
System.Collections.Generic.IEnumerator<string> System.Collections.Generic.IEnumerable<string>.GetEnumerator() => throw null;
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null;
|
||||
bool System.Collections.Generic.ICollection<string>.Contains(string item) => throw null;
|
||||
bool System.Collections.Generic.ICollection<string>.IsReadOnly { get => throw null; }
|
||||
bool System.Collections.Generic.ICollection<string>.Remove(string item) => throw null;
|
||||
int System.Collections.Generic.IList<string>.IndexOf(string item) => throw null;
|
||||
public bool Equals(Microsoft.Extensions.Primitives.StringValues other) => throw null;
|
||||
public bool Equals(string other) => throw null;
|
||||
public bool Equals(string[] other) => throw null;
|
||||
public int Count { get => throw null; }
|
||||
public override bool Equals(object obj) => throw null;
|
||||
public override int GetHashCode() => throw null;
|
||||
public override string ToString() => throw null;
|
||||
public static implicit operator Microsoft.Extensions.Primitives.StringValues(string value) => throw null;
|
||||
void System.Collections.Generic.ICollection<string>.Add(string item) => throw null;
|
||||
void System.Collections.Generic.ICollection<string>.Clear() => throw null;
|
||||
void System.Collections.Generic.ICollection<string>.CopyTo(string[] array, int arrayIndex) => throw null;
|
||||
void System.Collections.Generic.IList<string>.Insert(int index, string item) => throw null;
|
||||
void System.Collections.Generic.IList<string>.RemoveAt(int index) => throw null;
|
||||
public string this[int i] { get => throw null; set => throw null; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace System
|
||||
{
|
||||
// Generated from `System.Uri` in `System.Private.Uri, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a`
|
||||
public class Uri : System.Runtime.Serialization.ISerializable
|
||||
{
|
||||
public Uri(string uriString) => throw null;
|
||||
public override bool Equals(object comparand) => throw null;
|
||||
public override int GetHashCode() => throw null;
|
||||
public override string ToString() => throw null;
|
||||
void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) => throw null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,8 @@ class SuppressionComment extends Javadoc {
|
||||
isEolComment(this) and
|
||||
exists(string text | text = getChild(0).getText() |
|
||||
// match `lgtm[...]` anywhere in the comment
|
||||
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _) or
|
||||
annotation = text.regexpFind("(?i)\\blgtm\\s*\\[[^\\]]*\\]", _, _)
|
||||
or
|
||||
// match `lgtm` at the start of the comment and after semicolon
|
||||
annotation = text.regexpFind("(?i)(?<=^|;)\\s*lgtm(?!\\B|\\s*\\[)", _, _).trim()
|
||||
)
|
||||
|
||||
@@ -156,12 +156,16 @@ class LiveSpringBean extends SpringBean {
|
||||
// If the class does not exist for this bean, or the class is not a source bean, then this is
|
||||
// likely to be a definition using a library class, in which case we should consider it to be
|
||||
// live.
|
||||
not exists(getClass()) or
|
||||
not getClass().fromSource() or
|
||||
not exists(getClass())
|
||||
or
|
||||
not getClass().fromSource()
|
||||
or
|
||||
// In alfresco, "webscript" beans should be considered live
|
||||
getBeanParent*().getBeanParentName() = "webscript" or
|
||||
getBeanParent*().getBeanParentName() = "webscript"
|
||||
or
|
||||
// A live child bean implies this bean is live
|
||||
exists(LiveSpringBean child | this = child.getBeanParent()) or
|
||||
exists(LiveSpringBean child | this = child.getBeanParent())
|
||||
or
|
||||
// Beans constructed by a bean factory are considered live
|
||||
exists(SpringBeanFactory beanFactory | this = beanFactory.getAConstructedBean())
|
||||
)
|
||||
|
||||
@@ -41,7 +41,8 @@ predicate delegatingOverride(Method sub, Method sup) {
|
||||
stmt = sub.getBody().(SingletonBlock).getStmt() and
|
||||
(
|
||||
// ...that is either a delegating call to `sup` (with a possible cast)...
|
||||
delegatingSuperCall(stmt.(ExprStmt).getExpr(), sup) or
|
||||
delegatingSuperCall(stmt.(ExprStmt).getExpr(), sup)
|
||||
or
|
||||
// ...or a `return` statement containing such a call.
|
||||
delegatingSuperCall(stmt.(ReturnStmt).getResult(), sup)
|
||||
)
|
||||
|
||||
70
java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp
Normal file
70
java/ql/src/Security/CWE/CWE-022/ZipSlip.qhelp
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Extracting files from a malicious zip archive (or another archive format)
|
||||
without validating that the destination file path
|
||||
is within the destination directory can cause files outside the destination directory to be
|
||||
overwritten, due to the possible presence of directory traversal elements (<code>..</code>) in
|
||||
archive paths.</p>
|
||||
|
||||
<p>Zip archives contain archive entries representing each file in the archive. These entries
|
||||
include a file path for the entry, but these file paths are not restricted and may contain
|
||||
unexpected special elements such as the directory traversal element (<code>..</code>). If these
|
||||
file paths are used to determine an output file to write the contents of the archive item to, then
|
||||
the file may be written to an unexpected location. This can result in sensitive information being
|
||||
revealed or deleted, or an attacker being able to influence behavior by modifying unexpected
|
||||
files.</p>
|
||||
|
||||
<p>For example, if a zip file contains a file entry <code>..\sneaky-file</code>, and the zip file
|
||||
is extracted to the directory <code>c:\output</code>, then naively combining the paths would result
|
||||
in an output file path of <code>c:\output\..\sneaky-file</code>, which would cause the file to be
|
||||
written to <code>c:\sneaky-file</code>.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Ensure that output paths constructed from zip archive entries are validated to prevent writing
|
||||
files to unexpected locations.</p>
|
||||
|
||||
<p>The recommended way of writing an output file from a zip archive entry is to
|
||||
verify that the normalized full path of the output file starts with a prefix that matches the
|
||||
destination directory. Path normalization can be done with either
|
||||
<code>java.io.File.getCanonicalFile()</code> or <code>java.nio.file.Path.normalize()</code>.
|
||||
Prefix checking can be done with <code>String.startsWith(..)</code>, but it is better to use
|
||||
<code>java.nio.file.Path.startsWith(..)</code>, as the latter works on complete path segments.
|
||||
</p>
|
||||
|
||||
<p>Another alternative is to validate archive entries against a whitelist of expected files.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this example, a file path taken from a zip archive item entry is combined with a
|
||||
destination directory. The result is used as the destination file path without verifying that
|
||||
the result is within the destination directory. If provided with a zip file containing an archive
|
||||
path like <code>..\sneaky-file</code>, then this file would be written outside the destination
|
||||
directory.</p>
|
||||
|
||||
<sample src="ZipSlipBad.java" />
|
||||
|
||||
<p>To fix this vulnerability, we need to verify that the normalized <code>file</code> still has
|
||||
<code>destinationDir</code> as its prefix, and throw an exception if this is not the case.</p>
|
||||
|
||||
<sample src="ZipSlipGood.java" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Snyk:
|
||||
<a href="https://snyk.io/research/zip-slip-vulnerability">Zip Slip Vulnerability</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/Path_traversal">Path Traversal</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
176
java/ql/src/Security/CWE/CWE-022/ZipSlip.ql
Normal file
176
java/ql/src/Security/CWE/CWE-022/ZipSlip.ql
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @name Arbitrary file write during archive extraction ("Zip Slip")
|
||||
* @description Extracting files from a malicious archive without validating that the
|
||||
* destination file path is within the destination directory can cause files outside
|
||||
* the destination directory to be overwritten.
|
||||
* @kind problem
|
||||
* @id java/zipslip
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags security
|
||||
* external/cwe/cwe-022
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.dataflow.SSA
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import DataFlow
|
||||
|
||||
/**
|
||||
* A method that returns the name of an archive entry.
|
||||
*/
|
||||
class ArchiveEntryNameMethod extends Method {
|
||||
ArchiveEntryNameMethod() {
|
||||
exists(RefType archiveEntry |
|
||||
archiveEntry.hasQualifiedName("java.util.zip", "ZipEntry") or
|
||||
archiveEntry.hasQualifiedName("org.apache.commons.compress.archivers", "ArchiveEntry")
|
||||
|
|
||||
this.getDeclaringType().getASupertype*() = archiveEntry and
|
||||
this.hasName("getName")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that will be treated as the destination of a write.
|
||||
*/
|
||||
class WrittenFileName extends Expr {
|
||||
WrittenFileName() {
|
||||
// Constructors that write to their first argument.
|
||||
exists(ConstructorCall ctr | this = ctr.getArgument(0) |
|
||||
exists(Class c | ctr.getConstructor() = c.getAConstructor() |
|
||||
c.hasQualifiedName("java.io", "FileOutputStream") or
|
||||
c.hasQualifiedName("java.io", "RandomAccessFile") or
|
||||
c.hasQualifiedName("java.io", "FileWriter")
|
||||
)
|
||||
)
|
||||
or
|
||||
// Methods that write to their n'th argument
|
||||
exists(MethodAccess call, int n | this = call.getArgument(n) |
|
||||
call.getMethod().getDeclaringType().hasQualifiedName("java.nio.file", "Files") and
|
||||
(
|
||||
call.getMethod().getName().regexpMatch("new.*Reader|newOutputStream|create.*") and n = 0
|
||||
or
|
||||
call.getMethod().hasName("copy") and n = 1
|
||||
or
|
||||
call.getMethod().hasName("move") and n = 1
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n1` to `n2` is a dataflow step that converts between `String`,
|
||||
* `File`, and `Path`.
|
||||
*/
|
||||
predicate filePathStep(ExprNode n1, ExprNode n2) {
|
||||
exists(ConstructorCall cc | cc.getConstructedType() instanceof TypeFile |
|
||||
n1.asExpr() = cc.getAnArgument() and
|
||||
n2.asExpr() = cc
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, Method m |
|
||||
ma.getMethod() = m and
|
||||
n1.asExpr() = ma.getQualifier() and
|
||||
n2.asExpr() = ma
|
||||
|
|
||||
m.getDeclaringType() instanceof TypeFile and m.hasName("toPath")
|
||||
or
|
||||
m.getDeclaringType() instanceof TypePath and m.hasName("toAbsolutePath")
|
||||
)
|
||||
}
|
||||
|
||||
predicate fileTaintStep(ExprNode n1, ExprNode n2) {
|
||||
exists(MethodAccess ma, Method m |
|
||||
n1.asExpr() = ma.getQualifier() or
|
||||
n1.asExpr() = ma.getAnArgument()
|
||||
|
|
||||
n2.asExpr() = ma and
|
||||
ma.getMethod() = m and
|
||||
m.getDeclaringType() instanceof TypePath and
|
||||
m.hasName("resolve")
|
||||
)
|
||||
}
|
||||
|
||||
predicate localFileValueStep(Node n1, Node n2) {
|
||||
localFlowStep(n1, n2) or
|
||||
filePathStep(n1, n2)
|
||||
}
|
||||
|
||||
predicate localFileValueStepPlus(Node n1, Node n2) = fastTC(localFileValueStep/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* Holds if `check` is a guard that checks whether `var` is a file path with a
|
||||
* specific prefix when put in canonical form, thus guarding against ZipSlip.
|
||||
*/
|
||||
predicate validateFilePath(SsaVariable var, Guard check) {
|
||||
// `var.getCanonicalFile().toPath().startsWith(...)`,
|
||||
// `var.getCanonicalPath().startsWith(...)`, or
|
||||
// `var.toPath().normalize().startsWith(...)`
|
||||
exists(MethodAccess normalize, MethodAccess startsWith, Node n1, Node n2, Node n3, Node n4 |
|
||||
n1.asExpr() = var.getAUse() and
|
||||
n2.asExpr() = normalize.getQualifier() and
|
||||
(n1 = n2 or localFileValueStepPlus(n1, n2)) and
|
||||
n3.asExpr() = normalize and
|
||||
n4.asExpr() = startsWith.getQualifier() and
|
||||
(n3 = n4 or localFileValueStepPlus(n3, n4)) and
|
||||
check = startsWith and
|
||||
startsWith.getMethod().hasName("startsWith") and
|
||||
(
|
||||
normalize.getMethod().hasName("getCanonicalFile") or
|
||||
normalize.getMethod().hasName("getCanonicalPath") or
|
||||
normalize.getMethod().hasName("normalize")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` validates its `arg`th parameter.
|
||||
*/
|
||||
predicate validationMethod(Method m, int arg) {
|
||||
exists(Guard check, SsaImplicitInit var, ControlFlowNode exit, ControlFlowNode normexit |
|
||||
validateFilePath(var, check) and
|
||||
var.isParameterDefinition(m.getParameter(arg)) and
|
||||
exit = m and
|
||||
normexit.getANormalSuccessor() = exit and
|
||||
1 = strictcount(ControlFlowNode n | n.getANormalSuccessor() = exit)
|
||||
|
|
||||
check.(ConditionNode).getATrueSuccessor() = exit or
|
||||
check.controls(normexit.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
class ZipSlipConfiguration extends TaintTracking::Configuration {
|
||||
ZipSlipConfiguration() { this = "ZipSlip" }
|
||||
|
||||
override predicate isSource(Node source) {
|
||||
source.asExpr().(MethodAccess).getMethod() instanceof ArchiveEntryNameMethod
|
||||
}
|
||||
|
||||
override predicate isSink(Node sink) { sink.asExpr() instanceof WrittenFileName }
|
||||
|
||||
override predicate isAdditionalTaintStep(Node n1, Node n2) {
|
||||
filePathStep(n1, n2) or fileTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(Node node) {
|
||||
exists(Guard g, SsaVariable var, RValue varuse | validateFilePath(var, g) |
|
||||
varuse = node.asExpr() and
|
||||
varuse = var.getAUse() and
|
||||
g.controls(varuse.getBasicBlock(), true)
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma, int pos, RValue rv |
|
||||
validationMethod(ma.getMethod(), pos) and
|
||||
ma.getArgument(pos) = rv and
|
||||
adjacentUseUseSameVar(rv, node.asExpr()) and
|
||||
ma.getBasicBlock().bbDominates(node.asExpr().getBasicBlock())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from Node source, Node sink
|
||||
where any(ZipSlipConfiguration c).hasFlow(source, sink)
|
||||
select source, "Unsanitized archive entry, which may contain '..', is used in a $@.", sink,
|
||||
"file system operation"
|
||||
5
java/ql/src/Security/CWE/CWE-022/ZipSlipBad.java
Normal file
5
java/ql/src/Security/CWE/CWE-022/ZipSlipBad.java
Normal file
@@ -0,0 +1,5 @@
|
||||
void writeZipEntry(ZipEntry entry, File destinationDir) {
|
||||
File file = new File(destinationDir, entry.getName());
|
||||
FileOutputStream fos = new FileOutputStream(file); // BAD
|
||||
// ... write entry to fos ...
|
||||
}
|
||||
7
java/ql/src/Security/CWE/CWE-022/ZipSlipGood.java
Normal file
7
java/ql/src/Security/CWE/CWE-022/ZipSlipGood.java
Normal file
@@ -0,0 +1,7 @@
|
||||
void writeZipEntry(ZipEntry entry, File destinationDir) {
|
||||
File file = new File(destinationDir, entry.getName());
|
||||
if (!file.toPath().normalize().startsWith(destinationDir.toPath()))
|
||||
throw new Exception("Bad zip entry");
|
||||
FileOutputStream fos = new FileOutputStream(file); // OK
|
||||
// ... write entry to fos ...
|
||||
}
|
||||
@@ -58,7 +58,8 @@ predicate lessthanLength(ArrayAccess a) {
|
||||
pragma[nomagic]
|
||||
private Expr arrayReference(ArrayAccess arrayAccess) {
|
||||
// Array is stored in a variable.
|
||||
result = arrayAccess.getArray().(VarAccess).getVariable().getAnAccess() or
|
||||
result = arrayAccess.getArray().(VarAccess).getVariable().getAnAccess()
|
||||
or
|
||||
// Array is returned from a method.
|
||||
result.(MethodAccess).getMethod() = arrayAccess.getArray().(MethodAccess).getMethod()
|
||||
}
|
||||
|
||||
@@ -32,13 +32,15 @@ where
|
||||
not (
|
||||
(
|
||||
// The input has a lower bound.
|
||||
source.lowerBound() >= 0 or
|
||||
source.lowerBound() >= 0
|
||||
or
|
||||
// There is a condition dominating this expression ensuring that the index is >= 0.
|
||||
lowerBound(arrayAccess.getIndexExpr()) >= 0
|
||||
) and
|
||||
(
|
||||
// The input has an upper bound, and the array has a fixed size, and that fixed size is less.
|
||||
source.upperBound() < fixedArraySize(arrayAccess) or
|
||||
source.upperBound() < fixedArraySize(arrayAccess)
|
||||
or
|
||||
// There is a condition dominating this expression that ensures the index is less than the length.
|
||||
lessthanLength(arrayAccess)
|
||||
)
|
||||
|
||||
@@ -18,7 +18,8 @@ class HTTPString extends StringLiteral {
|
||||
exists(string s | this.getRepresentedString() = s |
|
||||
(
|
||||
// Either the literal "http", ...
|
||||
s = "http" or
|
||||
s = "http"
|
||||
or
|
||||
// ... or the beginning of a http URL.
|
||||
s.matches("http://%")
|
||||
) and
|
||||
|
||||
@@ -1266,7 +1266,8 @@ class VarAccess extends Expr, @varaccess {
|
||||
*/
|
||||
predicate isLocal() {
|
||||
// The access has no qualifier, or...
|
||||
not hasQualifier() or
|
||||
not hasQualifier()
|
||||
or
|
||||
// the qualifier is either `this` or `A.this`, where `A` is the enclosing type, or
|
||||
// the qualifier is either `super` or `A.super`, where `A` is the enclosing type.
|
||||
getQualifier().(InstanceAccess).isOwnInstanceAccess()
|
||||
@@ -1705,7 +1706,8 @@ class Argument extends Expr {
|
||||
p.isVarargs() and
|
||||
ptyp = p.getType() and
|
||||
(
|
||||
hasSubtype*(ptyp, typ) or
|
||||
hasSubtype*(ptyp, typ)
|
||||
or
|
||||
// If the types don't match then we'll guess based on whether there are type variables involved.
|
||||
hasInstantiation(ptyp.(Array).getComponentType())
|
||||
)
|
||||
|
||||
@@ -133,8 +133,8 @@ class TypeObjectOutputStream extends RefType {
|
||||
/** The class `java.nio.file.Paths`. */
|
||||
class TypePaths extends Class { TypePaths() { this.hasQualifiedName("java.nio.file", "Paths") } }
|
||||
|
||||
/** The class `java.nio.file.Path`. */
|
||||
class TypePath extends Class { TypePath() { this.hasQualifiedName("java.nio.file", "Path") } }
|
||||
/** The type `java.nio.file.Path`. */
|
||||
class TypePath extends RefType { TypePath() { this.hasQualifiedName("java.nio.file", "Path") } }
|
||||
|
||||
/** The class `java.nio.file.FileSystem`. */
|
||||
class TypeFileSystem extends Class {
|
||||
|
||||
@@ -370,7 +370,8 @@ class Method extends Callable, @method {
|
||||
}
|
||||
|
||||
override predicate isStrictfp() {
|
||||
Callable.super.isStrictfp() or
|
||||
Callable.super.isStrictfp()
|
||||
or
|
||||
// JLS 8.1.1.3, JLS 9.1.1.2
|
||||
getDeclaringType().isStrictfp()
|
||||
}
|
||||
@@ -575,21 +576,24 @@ class Field extends Member, ExprParent, @field, Variable {
|
||||
predicate isSourceDeclaration() { this.getSourceDeclaration() = this }
|
||||
|
||||
override predicate isPublic() {
|
||||
Member.super.isPublic() or
|
||||
Member.super.isPublic()
|
||||
or
|
||||
// JLS 9.3: Every field declaration in the body of an interface is
|
||||
// implicitly public, static, and final
|
||||
getDeclaringType() instanceof Interface
|
||||
}
|
||||
|
||||
override predicate isStatic() {
|
||||
Member.super.isStatic() or
|
||||
Member.super.isStatic()
|
||||
or
|
||||
// JLS 9.3: Every field declaration in the body of an interface is
|
||||
// implicitly public, static, and final
|
||||
this.getDeclaringType() instanceof Interface
|
||||
}
|
||||
|
||||
override predicate isFinal() {
|
||||
Member.super.isFinal() or
|
||||
Member.super.isFinal()
|
||||
or
|
||||
// JLS 9.3: Every field declaration in the body of an interface is
|
||||
// implicitly public, static, and final
|
||||
this.getDeclaringType() instanceof Interface
|
||||
|
||||
@@ -271,9 +271,11 @@ class NewInstance extends MethodAccess {
|
||||
not result instanceof TypeVariable and
|
||||
(
|
||||
// If this is called on a `Class<T>` instance, return the inferred type `T`.
|
||||
result = inferClassParameterType(getQualifier()) or
|
||||
result = inferClassParameterType(getQualifier())
|
||||
or
|
||||
// If this is called on a `Constructor<T>` instance, return the inferred type `T`.
|
||||
result = inferConstructorParameterType(getQualifier()) or
|
||||
result = inferConstructorParameterType(getQualifier())
|
||||
or
|
||||
// If the result of this is cast to a particular type, then use that type.
|
||||
result = getCastInferredConstructedTypes()
|
||||
)
|
||||
|
||||
@@ -216,16 +216,19 @@ private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
|
||||
|
|
||||
exists(RefType tUpperBound | tUpperBound = t.(Wildcard).getUpperBound().getType() |
|
||||
// ? extends T <= ? extends S if T <: S
|
||||
hasSubtypeStar0(s.(Wildcard).getUpperBound().getType(), tUpperBound) or
|
||||
hasSubtypeStar0(s.(Wildcard).getUpperBound().getType(), tUpperBound)
|
||||
or
|
||||
// ? extends T <= ?
|
||||
s.(Wildcard).isUnconstrained()
|
||||
)
|
||||
or
|
||||
exists(RefType tLowerBound | tLowerBound = t.(Wildcard).getLowerBound().getType() |
|
||||
// ? super T <= ? super S if s <: T
|
||||
hasSubtypeStar0(tLowerBound, s.(Wildcard).getLowerBound().getType()) or
|
||||
hasSubtypeStar0(tLowerBound, s.(Wildcard).getLowerBound().getType())
|
||||
or
|
||||
// ? super T <= ?
|
||||
s.(Wildcard).isUnconstrained() or
|
||||
s.(Wildcard).isUnconstrained()
|
||||
or
|
||||
// ? super T <= ? extends Object
|
||||
wildcardExtendsObject(s)
|
||||
)
|
||||
@@ -736,13 +739,15 @@ class NestedType extends RefType {
|
||||
}
|
||||
|
||||
override predicate isPublic() {
|
||||
super.isPublic() or
|
||||
super.isPublic()
|
||||
or
|
||||
// JLS 9.5: A member type declaration in an interface is implicitly public and static
|
||||
exists(Interface i | this = i.getAMember())
|
||||
}
|
||||
|
||||
override predicate isStrictfp() {
|
||||
super.isStrictfp() or
|
||||
super.isStrictfp()
|
||||
or
|
||||
// JLS 8.1.1.3, JLS 9.1.1.2
|
||||
getEnclosingType().isStrictfp()
|
||||
}
|
||||
@@ -762,11 +767,14 @@ class NestedType extends RefType {
|
||||
* section 8.9 (Enums) and section 9.5 (Member Type Declarations).
|
||||
*/
|
||||
override predicate isStatic() {
|
||||
super.isStatic() or
|
||||
super.isStatic()
|
||||
or
|
||||
// JLS 8.5.1: A member interface is implicitly static.
|
||||
this instanceof Interface or
|
||||
this instanceof Interface
|
||||
or
|
||||
// JLS 8.9: A nested enum type is implicitly static.
|
||||
this instanceof EnumType or
|
||||
this instanceof EnumType
|
||||
or
|
||||
// JLS 9.5: A member type declaration in an interface is implicitly public and static
|
||||
exists(Interface i | this = i.getAMember())
|
||||
}
|
||||
|
||||
@@ -148,7 +148,8 @@ class TestNGTestMethod extends Method {
|
||||
.getRepresentedString()
|
||||
|
|
||||
// Either the data provider should be on the current class, or a supertype
|
||||
getDeclaringType().getAnAncestor() = result.getDeclaringType() or
|
||||
getDeclaringType().getAnAncestor() = result.getDeclaringType()
|
||||
or
|
||||
// Or the data provider class should be declared
|
||||
result.getDeclaringType() = testAnnotation
|
||||
.getValue("dataProviderClass")
|
||||
|
||||
@@ -43,11 +43,14 @@ class LiveField extends SourceField {
|
||||
a.getValue(_) = access.getParent*()
|
||||
|
|
||||
// The annotated element is a live callable.
|
||||
isLive(a.getAnnotatedElement()) or
|
||||
isLive(a.getAnnotatedElement())
|
||||
or
|
||||
// The annotated element is in a live callable.
|
||||
isLive(a.getAnnotatedElement().(LocalVariableDecl).getEnclosingCallable()) or
|
||||
isLive(a.getAnnotatedElement().(LocalVariableDecl).getEnclosingCallable())
|
||||
or
|
||||
// The annotated element is a live field.
|
||||
a.getAnnotatedElement() instanceof LiveField or
|
||||
a.getAnnotatedElement() instanceof LiveField
|
||||
or
|
||||
// The annotated element is a live source class or interface.
|
||||
// Note: We ignore annotation values on library classes, because they should only refer to
|
||||
// fields in library classes, not `fromSource()` fields.
|
||||
|
||||
@@ -58,7 +58,8 @@ class CamelTargetClass extends Class {
|
||||
CamelTargetClass() {
|
||||
exists(SpringCamelXMLBeanRef camelXMLBeanRef |
|
||||
// A target may be defined by referencing an existing Spring Bean.
|
||||
this = camelXMLBeanRef.getRefBean().getClass() or
|
||||
this = camelXMLBeanRef.getRefBean().getClass()
|
||||
or
|
||||
// A target may be defined by referencing a class, which Apache Camel will create into a bean.
|
||||
this = camelXMLBeanRef.getBeanType()
|
||||
)
|
||||
|
||||
@@ -53,7 +53,8 @@ class MockitoInitedTest extends Class {
|
||||
MockitoInitedTest() {
|
||||
// Tests run with the Mockito runner.
|
||||
exists(RunWithAnnotation a | a = this.getAnAncestor().getAnAnnotation() |
|
||||
a.getRunner().(RefType).hasQualifiedName("org.mockito.runners", "MockitoJUnitRunner") or
|
||||
a.getRunner().(RefType).hasQualifiedName("org.mockito.runners", "MockitoJUnitRunner")
|
||||
or
|
||||
// Deprecated style.
|
||||
a.getRunner().(RefType).hasQualifiedName("org.mockito.runners", "MockitoJUnit44Runner")
|
||||
)
|
||||
@@ -124,7 +125,8 @@ class MockitoAnnotatedField extends Field {
|
||||
*/
|
||||
class MockitoMockedField extends MockitoAnnotatedField {
|
||||
MockitoMockedField() {
|
||||
hasAnnotation("org.mockito", "Mock") or
|
||||
hasAnnotation("org.mockito", "Mock")
|
||||
or
|
||||
// Deprecated style.
|
||||
hasAnnotation("org.mockito", "MockitoAnnotations$Mock")
|
||||
}
|
||||
|
||||
@@ -61,7 +61,8 @@ class FacesComponent extends Class {
|
||||
// Must be registered using either an annotation
|
||||
exists(FacesComponentAnnotation componentAnnotation |
|
||||
this = componentAnnotation.getFacesComponentClass()
|
||||
) or
|
||||
)
|
||||
or
|
||||
// Or in an XML file
|
||||
exists(FacesConfigComponentClass componentClassXML |
|
||||
this = componentClassXML.getFacesComponentClass()
|
||||
|
||||
@@ -153,9 +153,11 @@ class StatelessSessionEJB extends SessionEJB {
|
||||
class MessageDrivenBean extends EJB {
|
||||
MessageDrivenBean() {
|
||||
// Subtype of `javax.ejb.MessageBean`.
|
||||
this instanceof MessageBean or
|
||||
this instanceof MessageBean
|
||||
or
|
||||
// EJB annotations.
|
||||
this.getAnAnnotation().getType().hasName("MessageDriven") or
|
||||
this.getAnAnnotation().getType().hasName("MessageDriven")
|
||||
or
|
||||
// XML deployment descriptor.
|
||||
exists(EjbJarXMLFile f |
|
||||
this.getQualifiedName() = f
|
||||
@@ -173,7 +175,8 @@ class MessageDrivenBean extends EJB {
|
||||
class EntityEJB extends EJB {
|
||||
EntityEJB() {
|
||||
// Subtype of `javax.ejb.EntityBean`.
|
||||
this instanceof EntityBean or
|
||||
this instanceof EntityBean
|
||||
or
|
||||
// XML deployment descriptor.
|
||||
exists(EjbJarXMLFile f |
|
||||
this.getQualifiedName() = f
|
||||
@@ -294,7 +297,8 @@ class XmlSpecifiedBusinessInterface extends BusinessInterface {
|
||||
class AnnotatedBusinessInterface extends BusinessInterface {
|
||||
AnnotatedBusinessInterface() {
|
||||
// An interface annotated as `@Remote` or `@Local`.
|
||||
this.getAnAnnotation() instanceof BusinessInterfaceAnnotation or
|
||||
this.getAnAnnotation() instanceof BusinessInterfaceAnnotation
|
||||
or
|
||||
// An interface named within a `@Local` or `@Remote` annotation of another type.
|
||||
exists(BusinessInterfaceAnnotation a | a.getANamedType() = this)
|
||||
}
|
||||
|
||||
@@ -96,7 +96,8 @@ class SpringBasePackage extends string {
|
||||
class SpringComponentAnnotation extends AnnotationType {
|
||||
SpringComponentAnnotation() {
|
||||
// Component used directly as an annotation.
|
||||
hasQualifiedName("org.springframework.stereotype", "Component") or
|
||||
hasQualifiedName("org.springframework.stereotype", "Component")
|
||||
or
|
||||
// Component can be used as a meta-annotation on other annotation types.
|
||||
getAnAnnotation().getType() instanceof SpringComponentAnnotation
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import java
|
||||
class SpringControllerAnnotation extends AnnotationType {
|
||||
SpringControllerAnnotation() {
|
||||
// `@Controller` used directly as an annotation.
|
||||
hasQualifiedName("org.springframework.stereotype", "Controller") or
|
||||
hasQualifiedName("org.springframework.stereotype", "Controller")
|
||||
or
|
||||
// `@Controller` can be used as a meta-annotation on other annotation types.
|
||||
getAnAnnotation().getType() instanceof SpringControllerAnnotation
|
||||
}
|
||||
|
||||
@@ -111,7 +111,8 @@ private predicate fileSetWorldWritable(VarAccess fileAccess, Expr setWorldWritab
|
||||
setPosixPerms.getMethod().hasName("setPosixFilePermissions") and
|
||||
setPosixPerms.getMethod().getDeclaringType().hasQualifiedName("java.nio.file", "Files") and
|
||||
(
|
||||
fileAccess = setPosixPerms.getArgument(0) or
|
||||
fileAccess = setPosixPerms.getArgument(0)
|
||||
or
|
||||
// The argument was a file that has been converted to a path.
|
||||
fileAccess = getFileForPathConversion(setPosixPerms.getArgument(0))
|
||||
)
|
||||
|
||||
@@ -263,7 +263,8 @@ class PomDependency extends Dependency {
|
||||
source.getADependency() = this and
|
||||
// Consider dependencies that can be used at compile time.
|
||||
(
|
||||
getScope() = "compile" or
|
||||
getScope() = "compile"
|
||||
or
|
||||
// Provided dependencies are like compile time dependencies except (a) they are not packaged
|
||||
// when creating the jar and (b) they are not transitive.
|
||||
getScope() = "provided"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| ZipTest.java:7:19:7:33 | getName(...) | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipTest.java:9:48:9:51 | file | file system operation |
|
||||
| ZipTest.java:7:19:7:33 | getName(...) | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipTest.java:10:49:10:52 | file | file system operation |
|
||||
| ZipTest.java:7:19:7:33 | getName(...) | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipTest.java:11:36:11:39 | file | file system operation |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-022/ZipSlip.ql
|
||||
@@ -0,0 +1,54 @@
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
public class ZipTest {
|
||||
public void m1(ZipEntry entry, File dir) {
|
||||
String name = entry.getName();
|
||||
File file = new File(dir, name);
|
||||
FileOutputStream os = new FileOutputStream(file); // ZipSlip
|
||||
RandomAccessFile raf = new RandomAccessFile(file, "rw"); // ZipSlip
|
||||
FileWriter fw = new FileWriter(file); // ZipSlip
|
||||
}
|
||||
|
||||
public void m2(ZipEntry entry, File dir) {
|
||||
String name = entry.getName();
|
||||
File file = new File(dir, name);
|
||||
File canFile = file.getCanonicalFile();
|
||||
String canDir = dir.getCanonicalPath();
|
||||
if (!canFile.toPath().startsWith(canDir))
|
||||
throw new Exception();
|
||||
FileOutputStream os = new FileOutputStream(file); // OK
|
||||
}
|
||||
|
||||
public void m3(ZipEntry entry, File dir) {
|
||||
String name = entry.getName();
|
||||
File file = new File(dir, name);
|
||||
if (!file.toPath().normalize().startsWith(dir.toPath()))
|
||||
throw new Exception();
|
||||
FileOutputStream os = new FileOutputStream(file); // OK
|
||||
}
|
||||
|
||||
private void validate(File tgtdir, File file) {
|
||||
File canFile = file.getCanonicalFile();
|
||||
if (!canFile.toPath().startsWith(tgtdir.toPath()))
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
public void m4(ZipEntry entry, File dir) {
|
||||
String name = entry.getName();
|
||||
File file = new File(dir, name);
|
||||
validate(dir, file);
|
||||
FileOutputStream os = new FileOutputStream(file); // OK
|
||||
}
|
||||
|
||||
public void m5(ZipEntry entry, File dir) {
|
||||
String name = entry.getName();
|
||||
File file = new File(dir, name);
|
||||
Path absfile = file.toPath().toAbsolutePath().normalize();
|
||||
Path absdir = dir.toPath().toAbsolutePath().normalize();
|
||||
if (!absfile.startsWith(absdir))
|
||||
throw new Exception();
|
||||
FileOutputStream os = new FileOutputStream(file); // OK
|
||||
}
|
||||
}
|
||||
@@ -163,6 +163,12 @@ predicate whitelisted(UnusedLocal v) {
|
||||
isEnumMember(vd) or
|
||||
// ignore ambient declarations - too noisy
|
||||
vd.isAmbient()
|
||||
) or
|
||||
exists (DirectEval eval |
|
||||
// eval nearby
|
||||
eval.getEnclosingFunction() = v.getADeclaration().getEnclosingFunction() and
|
||||
// ... but not on the RHS
|
||||
not v.getAnAssignedExpr() = eval
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ class AMDModuleDefinition extends CallExpr {
|
||||
* parameters `pdep1` and `pdep2` correspond to dependencies
|
||||
* `dep1` and `dep2`.
|
||||
*/
|
||||
private SimpleParameter getDependencyParameter(string name) {
|
||||
Parameter getDependencyParameter(string name) {
|
||||
exists (PathExpr dep |
|
||||
dependencyParameter(dep, result) and
|
||||
dep.getValue() = name
|
||||
|
||||
@@ -418,11 +418,24 @@ private class BindPartialCall extends AdditionalPartialInvokeNode, DataFlow::Met
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial call through `_.partial` or a function with a similar interface.
|
||||
* A partial call through `_.partial`.
|
||||
*/
|
||||
private class LibraryPartialCall extends AdditionalPartialInvokeNode {
|
||||
LibraryPartialCall() {
|
||||
this = LodashUnderscore::member("partial").getACall() or
|
||||
private class LodashPartialCall extends AdditionalPartialInvokeNode {
|
||||
LodashPartialCall() {
|
||||
this = LodashUnderscore::member("partial").getACall()
|
||||
}
|
||||
|
||||
override predicate isPartialArgument(DataFlow::Node callback, DataFlow::Node argument, int index) {
|
||||
callback = getArgument(0) and
|
||||
argument = getArgument(index+1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial call through `ramda.partial`.
|
||||
*/
|
||||
private class RamdaPartialCall extends AdditionalPartialInvokeNode {
|
||||
RamdaPartialCall() {
|
||||
this = DataFlow::moduleMember("ramda", "partial").getACall()
|
||||
}
|
||||
|
||||
|
||||
@@ -399,10 +399,8 @@ class ModuleImportNode extends DataFlow::DefaultSourceNode {
|
||||
)
|
||||
or
|
||||
// declared AMD dependency
|
||||
exists (AMDModuleDefinition amd, PathExpr dep, Parameter p |
|
||||
amd.dependencyParameter(dep, p) and
|
||||
path = dep.getValue() and
|
||||
this = DataFlow::parameterNode(p)
|
||||
exists (AMDModuleDefinition amd |
|
||||
this = DataFlow::parameterNode(amd.getDependencyParameter(path))
|
||||
)
|
||||
or
|
||||
// AMD require
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import javascript
|
||||
import SyntacticHeuristics
|
||||
private import semmle.javascript.security.dataflow.CommandInjection
|
||||
|
||||
/**
|
||||
* A heuristic source of data flow in a security query.
|
||||
@@ -26,3 +27,13 @@ private class RemoteFlowPassword extends HeuristicSource, RemoteFlowSource {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A use of `JSON.stringify`, viewed as a source for command line injections
|
||||
* since it does not properly escape single quotes and dollar symbols.
|
||||
*/
|
||||
private class JSONStringifyAsCommandInjectionSource extends HeuristicSource, CommandInjection::Source {
|
||||
JSONStringifyAsCommandInjectionSource() {
|
||||
this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify")
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | esClient.js:7:13:7:18 | nj.foo |
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | esClient.js:10:13:10:17 | njFoo |
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | nodeJsClient.js:4:13:4:18 | nj.foo |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:9:15:9:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:15:15:15:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:21:15:21:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:27:15:27:15 | x |
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:27:16:27:16 | v |
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | esClient.js:7:13:7:18 | nj.foo |
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | esClient.js:10:13:10:17 | njFoo |
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | nodeJsClient.js:4:13:4:18 | nj.foo |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:9:15:9:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:15:15:15:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:21:15:21:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:27:15:27:15 | x |
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:27:16:27:16 | v |
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | esClient.js:7:13:7:18 | nj.foo |
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | esClient.js:10:13:10:17 | njFoo |
|
||||
| nodeJsLib.js:2:15:2:23 | "tainted" | nodeJsClient.js:4:13:4:18 | nj.foo |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:9:15:9:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:15:15:15:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:21:15:21:15 | x |
|
||||
| partial.js:5:15:5:24 | "tainted1" | partial.js:27:15:27:15 | x |
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:7:16:7:18 | val |
|
||||
| promises.js:2:16:2:24 | "tainted" | promises.js:38:32:38:32 | v |
|
||||
| promises.js:11:22:11:31 | "resolved" | promises.js:19:20:19:20 | v |
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
let underscore = require('underscore');
|
||||
let lodash = require('lodash');
|
||||
let R = require('ramda');
|
||||
|
||||
let source1 = "tainted1";
|
||||
let source2 = "tainted2";
|
||||
|
||||
function f1(x, y) {
|
||||
let sink1 = x;
|
||||
let sink2 = y;
|
||||
}
|
||||
f1.bind(null, source1)(source2);
|
||||
|
||||
function f2(x, y) {
|
||||
let sink1 = x;
|
||||
let sink2 = y;
|
||||
}
|
||||
underscore.partial(f2, source1)(source2);
|
||||
|
||||
function f3(x, y) {
|
||||
let sink1 = x;
|
||||
let sink2 = y;
|
||||
}
|
||||
lodash.partial(f3, source1)(source2);
|
||||
|
||||
function f4(x, y) {
|
||||
let sink1 = x;
|
||||
let sink2 = y;
|
||||
}
|
||||
R.partial(f4, [source1])(source2);
|
||||
@@ -1,2 +1,3 @@
|
||||
| additionalCommandInjections.js:2:28:2:35 | password |
|
||||
| sources.js:2:5:2:12 | password |
|
||||
| sources.js:3:5:3:20 | JSON.stringify() |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
(function() {
|
||||
password;
|
||||
JSON.stringify();
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
| Babelrc/importPragma.jsx:2:1:2:27 | import ... react'; | Unused import q. |
|
||||
| decorated.ts:1:1:1:126 | import ... where'; | Unused import actionHandler. |
|
||||
| decorated.ts:4:10:4:12 | fun | Unused function fun. |
|
||||
| eval.js:10:9:10:24 | not_used_by_eval | Unused variable not_used_by_eval. |
|
||||
| eval.js:19:9:19:24 | not_used_by_eval | Unused variable not_used_by_eval. |
|
||||
| externs.js:6:5:6:13 | iAmUnused | Unused variable iAmUnused. |
|
||||
| importWithoutPragma.jsx:1:1:1:27 | import ... react'; | Unused import h. |
|
||||
| multi-imports.js:1:1:1:29 | import ... om 'x'; | Unused imports a, b, d. |
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
(function(){
|
||||
var used_by_eval = f();
|
||||
eval(src);
|
||||
});
|
||||
(function(){
|
||||
eval(src);
|
||||
var used_by_eval = f();
|
||||
});
|
||||
(function(){
|
||||
var not_used_by_eval = f();
|
||||
(function(){
|
||||
eval(src);
|
||||
})
|
||||
});
|
||||
(function(){
|
||||
(function(){
|
||||
eval(src);
|
||||
})
|
||||
var not_used_by_eval = f();
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user