Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions cpp/ql/src/semmle/code/cpp/ir/dataflow/TaintTracking.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* Provides classes for performing local (intra-procedural) and
* global (inter-procedural) taint-tracking analyses.
*
* We define _taint propagation_ informally to mean that a substantial part of
* the information from the source is preserved at the sink. For example, taint
* propagates from `x` to `x + 100`, but it does not propagate from `x` to `x >
* 100` since we consider a single bit of information to be too little.
*/

import semmle.code.cpp.ir.dataflow.DataFlow
import semmle.code.cpp.ir.dataflow.DataFlow2
private import semmle.code.cpp.ir.IR

module TaintTracking {
/**
* A configuration of interprocedural taint tracking analysis. This defines
* sources, sinks, and any other configurable aspect of the analysis. Each
* use of the taint tracking library must define its own unique extension of
* this abstract class.
*
* A taint-tracking configuration is a special data flow configuration
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
* necessarily preserve values but are still relevant from a taint-tracking
* perspective. (For example, string concatenation, where one of the operands
* is tainted.)
*
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
* ```
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Optionally override `isSanitizer`.
* // Optionally override `isAdditionalTaintStep`.
* }
* ```
*
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
* ```
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
* Multiple configurations can coexist, but it is unsupported to depend on a
* `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
* overridden predicates that define sources, sinks, or additional steps.
* Instead, the dependency should go to a `TaintTracking::Configuration2` or
* a `DataFlow{2,3,4}::Configuration`.
*/
abstract class Configuration extends DataFlow::Configuration {
bindingset[this]
Configuration() { any() }

/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);

/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);

/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }

/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source, DataFlow::Node target) { none() }

final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }

final override predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}

/**
* A taint-tracking configuration that is backed by the `DataFlow2` library
* instead of `DataFlow`. Use this class when taint-tracking configurations
* or data-flow configurations must depend on each other.
*
* See `TaintTracking::Configuration` for the full documentation.
*/
abstract class Configuration2 extends DataFlow2::Configuration {
bindingset[this]
Configuration2() { any() }

/** Holds if `source` is a taint source. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSource(DataFlow::Node source);

/** Holds if `sink` is a taint sink. */
// overridden to provide taint-tracking specific qldoc
abstract override predicate isSink(DataFlow::Node sink);

/**
* Holds if taint should not flow into `node`.
*/
predicate isSanitizer(DataFlow::Node node) { none() }

/**
* Holds if the additional taint propagation step
* from `source` to `target` must be taken into account in the analysis.
* This step will only be followed if `target` is not in the `isSanitizer`
* predicate.
*/
predicate isAdditionalTaintStep(DataFlow::Node source, DataFlow::Node target) { none() }

final override predicate isBarrier(DataFlow::Node node) { isSanitizer(node) }

final override predicate isAdditionalFlowStep(DataFlow::Node source, DataFlow::Node target) {
this.isAdditionalTaintStep(source, target)
or
localTaintStep(source, target)
}
}

/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// Taint can flow into using ordinary data flow.
DataFlow::localFlowStep(nodeFrom, nodeTo)
or
// Taint can flow through expressions that alter the value but preserve
// more than one bit of it _or_ expressions that follow data through
// pointer indirections.
nodeTo.getAnOperand().getDefinitionInstruction() = nodeFrom and
(
nodeTo instanceof ArithmeticInstruction
or
nodeTo instanceof BitwiseInstruction

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should block taint propagation at bitwise and by default. It really requires both operands to be tainted before the result of the and is tainted. Anyway, let's defer that until later.

or
nodeTo instanceof PointerArithmeticInstruction
or
nodeTo instanceof FieldAddressInstruction
)
or
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
}

/**
* Holds if taint may propagate from `source` to `sink` in zero or more local
* (intra-procedural) steps.
*/
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
}
36 changes: 24 additions & 12 deletions cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ abstract class PointerArithmeticOpcode extends BinaryOpcode {}

abstract class PointerOffsetOpcode extends PointerArithmeticOpcode {}

abstract class ArithmeticOpcode extends Opcode {}

abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode {}

abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode {}

abstract class BitwiseOpcode extends Opcode {}

abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode {}

abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode {}

abstract class CompareOpcode extends BinaryOpcode {}

abstract class RelationalOpcode extends CompareOpcode {}
Expand Down Expand Up @@ -143,18 +155,18 @@ module Opcode {
class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue { override final string toString() { result = "CopyValue" } }
class Load extends CopyOpcode, OpcodeWithLoad, TLoad { override final string toString() { result = "Load" } }
class Store extends CopyOpcode, MemoryAccessOpcode, TStore { override final string toString() { result = "Store" } }
class Add extends BinaryOpcode, TAdd { override final string toString() { result = "Add" } }
class Sub extends BinaryOpcode, TSub { override final string toString() { result = "Sub" } }
class Mul extends BinaryOpcode, TMul { override final string toString() { result = "Mul" } }
class Div extends BinaryOpcode, TDiv { override final string toString() { result = "Div" } }
class Rem extends BinaryOpcode, TRem { override final string toString() { result = "Rem" } }
class Negate extends UnaryOpcode, TNegate { override final string toString() { result = "Negate" } }
class ShiftLeft extends BinaryOpcode, TShiftLeft { override final string toString() { result = "ShiftLeft" } }
class ShiftRight extends BinaryOpcode, TShiftRight { override final string toString() { result = "ShiftRight" } }
class BitAnd extends BinaryOpcode, TBitAnd { override final string toString() { result = "BitAnd" } }
class BitOr extends BinaryOpcode, TBitOr { override final string toString() { result = "BitOr" } }
class BitXor extends BinaryOpcode, TBitXor { override final string toString() { result = "BitXor" } }
class BitComplement extends UnaryOpcode, TBitComplement { override final string toString() { result = "BitComplement" } }
class Add extends BinaryArithmeticOpcode, TAdd { override final string toString() { result = "Add" } }
class Sub extends BinaryArithmeticOpcode, TSub { override final string toString() { result = "Sub" } }
class Mul extends BinaryArithmeticOpcode, TMul { override final string toString() { result = "Mul" } }
class Div extends BinaryArithmeticOpcode, TDiv { override final string toString() { result = "Div" } }
class Rem extends BinaryArithmeticOpcode, TRem { override final string toString() { result = "Rem" } }
class Negate extends UnaryArithmeticOpcode, TNegate { override final string toString() { result = "Negate" } }
class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft { override final string toString() { result = "ShiftLeft" } }
class ShiftRight extends BinaryBitwiseOpcode, TShiftRight { override final string toString() { result = "ShiftRight" } }
class BitAnd extends BinaryBitwiseOpcode, TBitAnd { override final string toString() { result = "BitAnd" } }
class BitOr extends BinaryBitwiseOpcode, TBitOr { override final string toString() { result = "BitOr" } }
class BitXor extends BinaryBitwiseOpcode, TBitXor { override final string toString() { result = "BitXor" } }
class BitComplement extends UnaryBitwiseOpcode, TBitComplement { override final string toString() { result = "BitComplement" } }
class LogicalNot extends UnaryOpcode, TLogicalNot { override final string toString() { result = "LogicalNot" } }
class CompareEQ extends CompareOpcode, TCompareEQ { override final string toString() { result = "CompareEQ" } }
class CompareNE extends CompareOpcode, TCompareNE { override final string toString() { result = "CompareNE" } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -898,67 +898,87 @@ class BinaryInstruction extends Instruction {
}
}

class AddInstruction extends BinaryInstruction {
class ArithmeticInstruction extends Instruction {
ArithmeticInstruction() {
getOpcode() instanceof ArithmeticOpcode
}
}

class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction {}

class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction {}

class AddInstruction extends BinaryArithmeticInstruction {
AddInstruction() {
getOpcode() instanceof Opcode::Add
}
}

class SubInstruction extends BinaryInstruction {
class SubInstruction extends BinaryArithmeticInstruction {
SubInstruction() {
getOpcode() instanceof Opcode::Sub
}
}

class MulInstruction extends BinaryInstruction {
class MulInstruction extends BinaryArithmeticInstruction {
MulInstruction() {
getOpcode() instanceof Opcode::Mul
}
}

class DivInstruction extends BinaryInstruction {
class DivInstruction extends BinaryArithmeticInstruction {
DivInstruction() {
getOpcode() instanceof Opcode::Div
}
}

class RemInstruction extends BinaryInstruction {
class RemInstruction extends BinaryArithmeticInstruction {
RemInstruction() {
getOpcode() instanceof Opcode::Rem
}
}

class NegateInstruction extends UnaryInstruction {
class NegateInstruction extends UnaryArithmeticInstruction {
NegateInstruction() {
getOpcode() instanceof Opcode::Negate
}
}

class BitAndInstruction extends BinaryInstruction {
class BitwiseInstruction extends Instruction {
BitwiseInstruction() {
getOpcode() instanceof BitwiseOpcode
}
}

class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction {}

class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction {}

class BitAndInstruction extends BinaryBitwiseInstruction {
BitAndInstruction() {
getOpcode() instanceof Opcode::BitAnd
}
}

class BitOrInstruction extends BinaryInstruction {
class BitOrInstruction extends BinaryBitwiseInstruction {
BitOrInstruction() {
getOpcode() instanceof Opcode::BitOr
}
}

class BitXorInstruction extends BinaryInstruction {
class BitXorInstruction extends BinaryBitwiseInstruction {
BitXorInstruction() {
getOpcode() instanceof Opcode::BitXor
}
}

class ShiftLeftInstruction extends BinaryInstruction {
class ShiftLeftInstruction extends BinaryBitwiseInstruction {
ShiftLeftInstruction() {
getOpcode() instanceof Opcode::ShiftLeft
}
}

class ShiftRightInstruction extends BinaryInstruction {
class ShiftRightInstruction extends BinaryBitwiseInstruction {
ShiftRightInstruction() {
getOpcode() instanceof Opcode::ShiftRight
}
Expand Down Expand Up @@ -1097,7 +1117,7 @@ class ConvertToDerivedInstruction extends InheritanceConversionInstruction {
}
}

class BitComplementInstruction extends UnaryInstruction {
class BitComplementInstruction extends UnaryBitwiseInstruction {
BitComplementInstruction() {
getOpcode() instanceof Opcode::BitComplement
}
Expand Down
Loading