Merge pull request #412 from adityasharad/merge/master-next-061118

Merge master into next.
This commit is contained in:
Arthur Baars
2018-11-06 18:46:50 +01:00
committed by GitHub
105 changed files with 3462 additions and 1462 deletions

View File

@@ -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* |

View File

@@ -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

View File

@@ -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. |

View File

@@ -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",

View File

@@ -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() {

View File

@@ -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

View File

@@ -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
}

View 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())
}
}

View File

@@ -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)
)
}

View File

@@ -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()
)

View File

@@ -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)
)
}

View File

@@ -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

View File

@@ -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
}

View 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())
}
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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())
)
)

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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())
)
)

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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
}

View 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())
}
}

View File

@@ -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)
}

View File

@@ -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)
)
}

View File

@@ -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()
)

View File

@@ -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)
)
}

View File

@@ -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)
}

View File

@@ -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()))
)
}

View File

@@ -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
}
}

View File

@@ -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" }
}

View File

@@ -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")
}
}

View File

@@ -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")
}
}

View File

@@ -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))
}
}
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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 |

View File

@@ -0,0 +1 @@
Security Features/CWE-079/XSS.ql

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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; }
}
}
}
}

View File

@@ -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 |

View File

@@ -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;

View File

@@ -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 |

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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()
)

View File

@@ -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())
)

View File

@@ -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)
)

View 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>

View 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"

View 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 ...
}

View 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 ...
}

View File

@@ -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()
}

View File

@@ -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)
)

View File

@@ -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

View File

@@ -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())
)

View File

@@ -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 {

View File

@@ -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

View File

@@ -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()
)

View File

@@ -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())
}

View File

@@ -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")

View File

@@ -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.

View File

@@ -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()
)

View File

@@ -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")
}

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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))
)

View File

@@ -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"

View File

@@ -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 |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-022/ZipSlip.ql

View File

@@ -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
}
}

View File

@@ -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
)
}

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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);

View File

@@ -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() |

View File

@@ -1,3 +1,4 @@
(function() {
password;
JSON.stringify();
})();

View File

@@ -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. |

View File

@@ -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