mirror of
https://github.com/hohn/codeql-workshop-dataflow-c.git
synced 2025-12-16 10:33:04 +01:00
Initial commit
This commit is contained in:
5
exercises/Exercise1.ql
Normal file
5
exercises/Exercise1.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import cpp
|
||||
|
||||
from ArrayExpr array
|
||||
where none()
|
||||
select array, "Access of dynamic input array."
|
||||
152
exercises/Exercise10.ql
Normal file
152
exercises/Exercise10.ql
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
int dyn_input_type_mem() { result = 1 }
|
||||
|
||||
int dyn_input_type_val() { result = 2 }
|
||||
|
||||
predicate typeValidationCallMatchesUse(TypeValidationCall call, DynamicInputAccess use) {
|
||||
exists(FieldAccess f, int expected |
|
||||
expected = call.getExpectedInputType(use.getArrayOffset().getValue().toInt()) and
|
||||
f.getQualifier() = use and
|
||||
(
|
||||
expected = dyn_input_type_mem() and f.getTarget().getName() = "ptr"
|
||||
or
|
||||
expected = dyn_input_type_val() and f.getTarget().getName() = "val"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
|
||||
predicate isTypeValidationGuardCompliant(GuardCondition gc, TypeValidationCall call) {
|
||||
typeValidationGuardOrIndirect(gc, call, _, this.getBasicBlock()) and
|
||||
typeValidationCallMatchesUse(call, this)
|
||||
}
|
||||
|
||||
predicate isTypeNotValidated() {
|
||||
not typeValidationGuardOrIndirect(_, _, _, this.getBasicBlock())
|
||||
or
|
||||
exists(TypeValidationCall call |
|
||||
typeValidationGuardOrIndirect(_, call, _, this.getBasicBlock()) and
|
||||
not typeValidationCallMatchesUse(call, this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
exists(Expr dest |
|
||||
DataFlow::localExprFlow(call, dest) and
|
||||
guardEnsuresEqUnordered(dest, other, guard, block, true) and
|
||||
InputTypesToTypeValidation::hasFlowToExpr(other)
|
||||
)
|
||||
}
|
||||
|
||||
predicate typeValidationGuardOrIndirect(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
typeValidationGuard(guard, call, other, block) or
|
||||
typeValidationGuardOrIndirect(guard, call, other,
|
||||
block.getEnclosingFunction().getACallToThisFunction().getBasicBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction f | f.getInputTypesParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// avoid non-monotonic recursion, as `typeValidationGuard` depends on this config
|
||||
sink.asExpr() instanceof VariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccess = DataFlow::Make<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
access.isTypeNotValidated()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
from InputToAccess::PathNode source, InputToAccess::PathNode sink
|
||||
where InputToAccess::hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Access to dynamic input array with missing or mismatched type validation."
|
||||
154
exercises/Exercise11.ql
Normal file
154
exercises/Exercise11.ql
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
int dyn_input_type_mem() { result = 1 }
|
||||
|
||||
int dyn_input_type_val() { result = 2 }
|
||||
|
||||
predicate typeValidationCallMatchesUse(TypeValidationCall call, DynamicInputAccess use) {
|
||||
exists(FieldAccess f, int expected |
|
||||
expected = call.getExpectedInputType(use.getArrayOffset().getValue().toInt()) and
|
||||
f.getQualifier() = use and
|
||||
(
|
||||
expected = dyn_input_type_mem() and f.getTarget().getName() = "ptr"
|
||||
or
|
||||
expected = dyn_input_type_val() and f.getTarget().getName() = "val"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
|
||||
predicate isTypeNotValidated() {
|
||||
not typeValidationGuardOrIndirect(_, _, _, this.getBasicBlock())
|
||||
or
|
||||
exists(TypeValidationCall call |
|
||||
typeValidationGuardOrIndirect(_, call, _, this.getBasicBlock()) and
|
||||
not typeValidationCallMatchesUse(call, this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
exists(Expr dest |
|
||||
DataFlow::localExprFlow(call, dest) and
|
||||
guardEnsuresEqUnordered(dest, other, guard, block, true) and
|
||||
InputTypesToTypeValidation::hasFlowToExpr(other)
|
||||
)
|
||||
}
|
||||
|
||||
predicate typeValidationGuardOrIndirect(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
typeValidationGuard(guard, call, other, block) or
|
||||
typeValidationGuardOrIndirect(guard, call, other,
|
||||
block.getEnclosingFunction().getACallToThisFunction().getBasicBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction f | f.getInputTypesParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// avoid non-monotonic recursion, as `typeValidationGuard` depends on this config
|
||||
sink.asExpr() instanceof VariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccess = DataFlow::Make<InputToAccessConfig>;
|
||||
|
||||
int explorationLimit() { result = 2 }
|
||||
|
||||
module InputToAccessFlowExploration = InputToAccess::FlowExploration<explorationLimit/0>;
|
||||
|
||||
import InputToAccessFlowExploration::PartialPathGraph
|
||||
|
||||
module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
access.isTypeNotValidated()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
InputToAccessFlowExploration::PartialPathNode source,
|
||||
InputToAccessFlowExploration::PartialPathNode mid, int dist
|
||||
where
|
||||
InputToAccessFlowExploration::hasPartialFlow(source, mid, dist) and
|
||||
// not a sink
|
||||
not InputToAccessConfig::isSink(mid.getNode()) and
|
||||
// no flow from mid
|
||||
not DataFlow::localFlowStep(mid.getNode(), _) and
|
||||
// more than 1 interprocedural step from source
|
||||
dist >= 1
|
||||
select mid, source, mid, "Partial flow"
|
||||
147
exercises/Exercise12.ql
Normal file
147
exercises/Exercise12.ql
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
int dyn_input_type_mem() { result = 1 }
|
||||
|
||||
int dyn_input_type_val() { result = 2 }
|
||||
|
||||
predicate typeValidationCallMatchesUse(TypeValidationCall call, DynamicInputAccess use) {
|
||||
exists(FieldAccess f, int expected |
|
||||
expected = call.getExpectedInputType(use.getArrayOffset().getValue().toInt()) and
|
||||
f.getQualifier() = use and
|
||||
(
|
||||
expected = dyn_input_type_mem() and f.getTarget().getName() = "ptr"
|
||||
or
|
||||
expected = dyn_input_type_val() and f.getTarget().getName() = "val"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
|
||||
predicate isTypeNotValidated() {
|
||||
not typeValidationGuardOrIndirect(_, _, _, this.getBasicBlock())
|
||||
or
|
||||
exists(TypeValidationCall call |
|
||||
typeValidationGuardOrIndirect(_, call, _, this.getBasicBlock()) and
|
||||
not typeValidationCallMatchesUse(call, this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
exists(Expr dest |
|
||||
DataFlow::localExprFlow(call, dest) and
|
||||
guardEnsuresEqUnordered(dest, other, guard, block, true) and
|
||||
InputTypesToTypeValidation::hasFlowToExpr(other)
|
||||
)
|
||||
}
|
||||
|
||||
predicate typeValidationGuardOrIndirect(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
typeValidationGuard(guard, call, other, block) or
|
||||
typeValidationGuardOrIndirect(guard, call, other,
|
||||
block.getEnclosingFunction().getACallToThisFunction().getBasicBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction f | f.getInputTypesParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// avoid non-monotonic recursion, as `typeValidationGuard` depends on this config
|
||||
sink.asExpr() instanceof VariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccess = DataFlow::Make<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
access.isTypeNotValidated()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
from InputToAccess::PathNode source, InputToAccess::PathNode sink
|
||||
where InputToAccess::hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Access to dynamic input array with missing or mismatched type validation."
|
||||
49
exercises/Exercise2.ql
Normal file
49
exercises/Exercise2.ql
Normal file
@@ -0,0 +1,49 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
// copy your solution from Exercise 1
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
|
||||
from DynamicInputAccess access
|
||||
where not typeValidationGuard(_, _, _, access.getBasicBlock())
|
||||
select access, "Access to dynamic input array without type validation."
|
||||
6
exercises/Exercise3.ql
Normal file
6
exercises/Exercise3.ql
Normal file
@@ -0,0 +1,6 @@
|
||||
import cpp
|
||||
|
||||
from Function f
|
||||
// replace this
|
||||
where none()
|
||||
select f, "replace this", "replace this"
|
||||
102
exercises/Exercise4.ql
Normal file
102
exercises/Exercise4.ql
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
// replace this with your solution from Exercise 2
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccess = DataFlow::Make<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// replace this
|
||||
// hint: use your logic from Exercise 2
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
from InputToAccess::PathNode source, InputToAccess::PathNode sink
|
||||
where InputToAccess::hasFlowPath(source, sink)
|
||||
select sink, source, sink, "Access to dynamic input array without type validation."
|
||||
106
exercises/Exercise5.ql
Normal file
106
exercises/Exercise5.ql
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
exists(Expr dest |
|
||||
DataFlow::localExprFlow(call, dest) and
|
||||
guardEnsuresEqUnordered(dest, other, guard, block, true) and
|
||||
InputTypesToTypeValidation::hasFlowToExpr(other)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction f | f.getInputTypesParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// avoid non-monotonic recursion, as `typeValidationGuard` depends on this config
|
||||
sink.asExpr() instanceof VariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccess = DataFlow::Make<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DynamicInputAccess access | sink.asExpr() = access.getArrayBase())
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
from InputToAccess::PathNode source, InputToAccess::PathNode sink
|
||||
where InputToAccess::hasFlowPath(source, sink)
|
||||
select sink, source, sink, "Access to dynamic input array without type validation."
|
||||
90
exercises/Exercise7.ql
Normal file
90
exercises/Exercise7.ql
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
exists(Expr dest |
|
||||
DataFlow::localExprFlow(call, dest) and
|
||||
guardEnsuresEqUnordered(dest, other, guard, block, true) and
|
||||
InputTypesToTypeValidation::hasFlowToExpr(other)
|
||||
)
|
||||
}
|
||||
|
||||
predicate typeValidationGuardOrIndirect(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction f | f.getInputTypesParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// avoid non-monotonic recursion, as `typeValidationGuard` depends on this config
|
||||
sink.asExpr() instanceof VariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
from DynamicInputAccess access, GuardCondition gc
|
||||
where typeValidationGuardOrIndirect(gc, _, _, access.getBasicBlock())
|
||||
select access, gc
|
||||
105
exercises/Exercise8.ql
Normal file
105
exercises/Exercise8.ql
Normal file
@@ -0,0 +1,105 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
int dyn_input_type_mem() { result = 1 }
|
||||
|
||||
int dyn_input_type_val() { result = 2 }
|
||||
|
||||
predicate typeValidationCallMatchesUse(TypeValidationCall call, DynamicInputAccess use) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
|
||||
predicate isTypeNotValidated() {
|
||||
// replace this and include mismatched validation
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
int getExpectedInputType(int input_index) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
exists(Expr dest |
|
||||
DataFlow::localExprFlow(call, dest) and
|
||||
guardEnsuresEqUnordered(dest, other, guard, block, true) and
|
||||
InputTypesToTypeValidation::hasFlowToExpr(other)
|
||||
)
|
||||
}
|
||||
|
||||
predicate typeValidationGuardOrIndirect(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
typeValidationGuard(guard, call, other, block) or
|
||||
typeValidationGuardOrIndirect(guard, call, other,
|
||||
block.getEnclosingFunction().getACallToThisFunction().getBasicBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction f | f.getInputTypesParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// avoid non-monotonic recursion, as `typeValidationGuard` depends on this config
|
||||
sink.asExpr() instanceof VariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
from DynamicInputAccess access
|
||||
// replace this
|
||||
where none()
|
||||
select access
|
||||
140
exercises/Exercise9.ql
Normal file
140
exercises/Exercise9.ql
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
int dyn_input_type_mem() { result = 1 }
|
||||
|
||||
int dyn_input_type_val() { result = 2 }
|
||||
|
||||
predicate typeValidationCallMatchesUse(TypeValidationCall call, DynamicInputAccess use) {
|
||||
exists(FieldAccess f, int expected |
|
||||
expected = call.getExpectedInputType(use.getArrayOffset().getValue().toInt()) and
|
||||
f.getQualifier() = use and
|
||||
(
|
||||
expected = dyn_input_type_mem() and f.getTarget().getName() = "ptr"
|
||||
or
|
||||
expected = dyn_input_type_val() and f.getTarget().getName() = "val"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An access of a dynamic input array (of type `dyn_input_t`)
|
||||
*/
|
||||
class DynamicInputAccess extends ArrayExpr {
|
||||
DynamicInputAccess() {
|
||||
this.getArrayBase().getType().(DerivedType).getBaseType().getName() = "dyn_input_t"
|
||||
}
|
||||
|
||||
predicate isTypeNotValidated() {
|
||||
not typeValidationGuardOrIndirect(_, _, _, this.getBasicBlock())
|
||||
or
|
||||
exists(TypeValidationCall call |
|
||||
typeValidationGuardOrIndirect(_, call, _, this.getBasicBlock()) and
|
||||
not typeValidationCallMatchesUse(call, this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op1` and `op2` are checked for equality in any order
|
||||
* with no distinction between left and right operands of the equality check
|
||||
*/
|
||||
predicate guardEnsuresEqUnordered(
|
||||
Expr op1, Expr op2, GuardCondition guard, BasicBlock block, boolean areEqual
|
||||
) {
|
||||
guard.ensuresEq(op1, op2, 0, block, areEqual) or
|
||||
guard.ensuresEq(op2, op1, 0, block, areEqual)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relates a `call` to a `guard`, which uses the result of the call to validate
|
||||
* equality of the result of `call` against `other` to guard `block`.
|
||||
*/
|
||||
predicate typeValidationGuard(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
exists(Expr dest |
|
||||
DataFlow::localExprFlow(call, dest) and
|
||||
guardEnsuresEqUnordered(dest, other, guard, block, true) and
|
||||
InputTypesToTypeValidation::hasFlowToExpr(other)
|
||||
)
|
||||
}
|
||||
|
||||
predicate typeValidationGuardOrIndirect(
|
||||
GuardCondition guard, TypeValidationCall call, Expr other, BasicBlock block
|
||||
) {
|
||||
typeValidationGuard(guard, call, other, block) or
|
||||
typeValidationGuardOrIndirect(guard, call, other,
|
||||
block.getEnclosingFunction().getACallToThisFunction().getBasicBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input_types` parameter to a subsequent use in a GuardCondition.
|
||||
*/
|
||||
module InputTypesToTypeValidation = DataFlow::Make<InputTypesToTypeValidationConfig>;
|
||||
|
||||
module InputTypesToTypeValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction f | f.getInputTypesParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// avoid non-monotonic recursion, as `typeValidationGuard` depends on this config
|
||||
sink.asExpr() instanceof VariableAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An entrypoint that has a `dyn_array_t[2]` `input` parameter and
|
||||
* `input_types` parameter.
|
||||
*/
|
||||
class EntrypointFunction extends Function {
|
||||
EntrypointFunction() { this.hasName(["EP_copy_mem", "EP_print_val", "EP_write_val_to_mem"]) }
|
||||
|
||||
Parameter getInputParameter() { result = this.getParameter(0) }
|
||||
|
||||
Parameter getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccess = DataFlow::Make<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
// replace this
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
from InputToAccess::PathNode source, InputToAccess::PathNode sink
|
||||
where InputToAccess::hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Access to dynamic input array with missing or mismatched type validation."
|
||||
6
exercises/qlpack.yml
Normal file
6
exercises/qlpack.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
library: false
|
||||
name: exercises
|
||||
version: 0.0.1
|
||||
dependencies:
|
||||
codeql/cpp-all: 0.6.1
|
||||
Reference in New Issue
Block a user