Files
codeql-workshop-dataflow-c/solutions/Exercise5.ql
Nikita Kraiouchkine 850d9d296a Initial commit
2023-04-17 10:27:44 +02:00

106 lines
3.2 KiB
Plaintext

/**
* @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) {
typeValidationGuard(_, _, _, node.asExpr().getBasicBlock())
}
}
from InputToAccess::PathNode source, InputToAccess::PathNode sink
where InputToAccess::hasFlowPath(source, sink)
select sink, source, sink, "Access to dynamic input array without type validation."