mirror of
https://github.com/hohn/codeql-workshop-dataflow-c.git
synced 2025-12-15 18:23:04 +01:00
Add stateful data-flow (flow state) content
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -9,4 +9,5 @@ cpp-dataflow-part1-database
|
||||
cpp-dataflow-part2-database
|
||||
cpp-dataflow-part3-database
|
||||
settings.json
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
*.code-workspace
|
||||
|
||||
93
README.md
93
README.md
@@ -79,7 +79,7 @@ Before use, `input_types` must be validated against an expected value. If no suc
|
||||
|
||||
## Part 1: Introduction to Data-Flow Analysis by Modeling Missing Type Validation
|
||||
### Exercise 1
|
||||
Output all expressions that access a `dynamic_input_t` array. Accessing dynamic inputs involves an `ArrayExpr` where the base type of the array type accessed is `dynamic_input_t`. These expressions can be thought of as "sinks" for the purpose of data-flow analysis.
|
||||
Output all expressions that access a `dyn_input_t` array. Accessing dynamic inputs involves an `ArrayExpr` where the base type of the array type accessed is `dyn_input_t`. These expressions can be thought of as "sinks" for the purpose of data-flow analysis.
|
||||
|
||||
<details>
|
||||
<summary>Hint</summary>
|
||||
@@ -105,7 +105,7 @@ if(expected_types != input_types) {
|
||||
// access
|
||||
```
|
||||
|
||||
Validation involves a comparison of the `input_types` parameter with a value returned from `DYN_INPUT_TYPE`, where the arguments to that call dictate the expected types of the `dynamic_input_t` array.
|
||||
Validation involves a comparison of the `input_types` parameter with a value returned from `DYN_INPUT_TYPE`, where the arguments to that call dictate the expected types of the `dyn_input_t` array.
|
||||
|
||||
There are three steps to this exercise:
|
||||
1. Find all calls to `DYN_INPUT_TYPE`.
|
||||
@@ -140,7 +140,7 @@ void func(dyn_input_t *input, unsigned int input_types, int irrelevant_param) {
|
||||
}
|
||||
```
|
||||
|
||||
The solution is global data-flow analysis. Start by modeling the source, i.e. the entrypoints and their parameters. Entrypoints are functions that are not called from anywhere else in the program and match the signature `int(dynamic_input_t*, unsigned int)`.
|
||||
The solution is global data-flow analysis. Start by modeling the source, i.e. the entrypoints and their parameters. Entrypoints are functions that are not called from anywhere else in the program and match the signature `int(dyn_input_t*, unsigned int)`.
|
||||
|
||||
Specifically, these functions are:
|
||||
- `EP_copy_mem`
|
||||
@@ -309,12 +309,11 @@ What is the missing edge?
|
||||
</details>
|
||||
|
||||
## Exercise 12
|
||||
In this final exercise for Part 2, in the global data-flow configuration, add an additional flow step between the `input` parameter and the `output` parameter of calls to `lock_input` to add the missing edges.
|
||||
In this final exercise for Part 2, in the global data-flow configuration, add an additional flow step between the `input` parameter and the result of calls to `lock_input` (represented as the `FunctionCall` itself) to add the missing edges.
|
||||
|
||||
<details>
|
||||
<summary>Hint</summary>
|
||||
|
||||
* Use `Node::asDefiningArgument` to model the `output` parameter.
|
||||
* Use `Node::asExpr` to model the `input` parameter.
|
||||
|
||||
</details>
|
||||
@@ -323,47 +322,75 @@ You should now see additional results through flow paths that include `lock_inpu
|
||||
|
||||
Optional: Re-add the barrier checks from `Exercise10` to see their impact on a larger set of results.
|
||||
|
||||
# End of workshop
|
||||
|
||||
This workshop is currently in ongoing development. Planned topics include:
|
||||
- Outputting flow state context for each result (currently, state is only bound inside the data-flow configuration).
|
||||
- Using runtime value analysis to identify type checks with patterns differing from `DYN_INPUT_TYPE`.
|
||||
|
||||
## Part 3: Flow-State
|
||||
The second part of this workshop built upon the data-flow and control-flow analysis from the first part to further identify cases where type validation exists but does not match the subsequent access type. However, that rudimentary recursive control-flow analysis might not work in all cases, as it does not preserve flow path state in the case of multiple function calls with varying validation states.
|
||||
|
||||
To preserve the state of type validation checks across each data-flow path, we can use flow-state.
|
||||
|
||||
### Exercise 13
|
||||
Flow states are defined by a class extending `DataFlow::FlowState`. Within a data-flow configuration, an initial flow state is specified in the `isSource` predicate and can be subsequently updated in the `isAdditionalFlowStep` and `isBarrier` predicates. The `isSink` predicate can then check the (path-sensitive) flow-state and conditionally restrict sinks based on the state on the path to the sink.
|
||||
Definitions modelling the two possible states of type validation for a flow path to a `DynamicInputAccess`, validated and unvalidated, have been provided for you in this exercise. These states have been defined using `newtype` in order to create the [algebraic datatype](https://codeql.github.com/docs/ql-language-reference/types/#algebraic-datatypes) `TTypeValidationState`, which represents the possible states of type validation for a flow path to a `DynamicInputAccess`. Three classes have also been defined to extend from it using this [standard pattern](https://codeql.github.com/docs/ql-language-reference/types/#standard-pattern-for-using-algebraic-datatypes). Try to understand the syntax and the way these types have been structured before proceeding.
|
||||
|
||||
First define the necessary flow-state classes:
|
||||
1. Define the flow state `InputTypesNotValidated` (hint: just use FlowStateString::FlowStateEmpty).
|
||||
2. Define the flow state `InputTypesValidated` with a [field declaration](https://codeql.github.com/docs/ql-language-reference/types/#fields) specifying the type validation call. Implement a `getValidatedType()` predicate to return the field.
|
||||
Secondly, the `InputToAccessConfig` data-flow configuration has been modified from the previous exercise to be stateful. Note the following four changes:
|
||||
1. `InputToAccessConfig` now implements `DataFlow::StateConfigSig` rather than `DataFlow::ConfigSig`.
|
||||
1. `InputToAccessConfig` now requires some changes to its predicate signatures to include state parameters.
|
||||
1. `InputToAccessConfig` requires a declaration of a `FlowState` type, which in this example, is an alias to `TTypeValidationState`.
|
||||
1. `InputToAccess` now constructs a data-flow computation using `DataFlow::MakeWithState<InputToAccessConfig>` rather than `DataFlow::Make<InputToAccessConfig>`.
|
||||
|
||||
Note the changes in the data-flow configuration in Exercise13.ql: some predicates now have one or more `state` parameters. Complete the predicates to implement the flow-state logic.
|
||||
It helps to approach implementing each predicate with an understanding of its purpose in stateful data-flow:
|
||||
| Predicate | Standard use-pattern |
|
||||
|--------------------------|----------------------------------------------------------|
|
||||
| `isSource` | Bind `state` to an initial state |
|
||||
| `isSink` | Restrict sinks to those on paths with a specific `state` |
|
||||
| `isAdditionalFlowStep/4` | Define state changes from `node1` to `node2` |
|
||||
| `isAdditionalFlowStep/2` | As before, define flow steps without a state change |
|
||||
| `isBarrier` | Block flow through `node` with a specific `state` |
|
||||
|
||||
For now, in `isSink`, only check if the state is `InputTypesNotValidated`.
|
||||
Reimplement the new `isSource` and `isSink` predicates to bind state to `TypeUnvalidatedState`. Ignore the `isAdditionalFlowStep/4` and `isBarrier/2` predicates for now; they will be used in a later exercise.
|
||||
|
||||
<details>
|
||||
<summary>Hint</summary>
|
||||
|
||||
* `isSource` should check if state is an `instanceof` `InputTypesNotValidated`.
|
||||
* In `isAdditionalFlowStep`, we don't actually want to add another additional flow step. However, we should restrict the new state to be an `instanceof InputTypesValidated`, where `InputTypesValidated::getValidatedType()` is derived from
|
||||
the call column of `typeValidationGuard`.
|
||||
|
||||
</details>
|
||||
|
||||
Output all flow paths. You should see all results that do not have type validation.
|
||||
You should see the same results as in Exercise 12.
|
||||
|
||||
### Exercise 14
|
||||
In this final exercise, complete the `isSink` predicate to include cases where the state is `InputTypesValidated` and the expected type *does not* match the access type.
|
||||
There are two primary ways to define state along a path:
|
||||
1. Bind an initial state based on the source.
|
||||
1. Change a state along a path based on a step between two nodes.
|
||||
|
||||
You should now see path-sensitive results with no false-negatives or false-positives.
|
||||
Those two approaches can also be used together.
|
||||
|
||||
<details>
|
||||
<summary>Hint</summary>
|
||||
In this workshop, we will use the second approach: changing state along a path. Start by defining those state-changing steps as part of the `TypeValidatedState` class. Modeling and exposing the state changing step as part of the flow state class is a common pattern used frequently in the CodeQL standard library.
|
||||
|
||||
* Use `typeValidationCallMatchesUse` in `isSink`.
|
||||
Complete the characteristic predicate of `TypeValidatedState` to bind all of its field declarations, where:
|
||||
* `node1` flows to `node2` in one step
|
||||
* `node1` is not guarded by a validation check that `node2` is guarded by
|
||||
* `call` is the call in the validation check `node2` is guarded by
|
||||
|
||||
</details>
|
||||
Use `DataFlow::localFlowStep` to model the local flow step.
|
||||
|
||||
You should see 69 results.
|
||||
|
||||
### Exercise 15
|
||||
Next, combine the class written in Exercise 14 with the stateful data-flow introduced in Exercise 13. Rather than recursively checking if a sink/access is type-validated, introduce that check along the flow path with `isAdditionalFlowStep/4`.
|
||||
|
||||
Reimplement the `isSink`, `isAdditionalFlowStep/4`, and `isBarrier` predicates.
|
||||
|
||||
**Important**:
|
||||
A peculiarity of flow-state is that modelling a state change in `isAdditionalFlowStep` for an edge that exists independently of this additional flow step created results in a duplicate subsequent flow path for *both* states. Therefore, `isBarrier` must be used to block that duplicate subsequent flow for the unwanted old state. Take the following example as a demonstration of this behavior. Assume that two states exist: `InitialState` and `ChangedState`. If `state_change(node)` is referenced in `isAdditionalFlowStep` but a matching reference in `isBarrier` for the `state_change` with `state instanceof InitialState` is missing, the flow state will be as follows:
|
||||
```cpp
|
||||
void flow_state_example(char* node) {
|
||||
*node; // state instanceof InitialState
|
||||
state_change(node); // arg_state instanceof InitialState,
|
||||
// return_state instanceof [InitialState, ChangedState]
|
||||
*node; // state instanceof [InitialState, ChangedState]
|
||||
}
|
||||
```
|
||||
|
||||
**Hint:**
|
||||
Make sure to add a barrier to nodes on paths with a validated state but that are guarded by a type check with a type validation call not matching the call of the state (`state.(TypeValidatedState).getCall() != call_guarding_node`).
|
||||
|
||||
You should now see 22 results.
|
||||
|
||||
### Exercise 16
|
||||
Lastly, refine the output message of your query by accessing the state of the sink node. Use an `if/then/else` statement in the `where` clause of the query to check if the sink is validated or unvalidated and output a descriptive message for each case accordingly.
|
||||
|
||||
You can retrieve the state of a path node with `DataFlow::PathNode::getState()`. You can cast that state to the appropriate derived type if necessary.
|
||||
|
||||
You should see the same 22 results but with more descriptive messages.
|
||||
186
exercises/Exercise13.ql
Normal file
186
exercises/Exercise13.ql
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @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") }
|
||||
|
||||
/**
|
||||
* Returns the integer value of the expected type specified for the element at `input_index`
|
||||
*/
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState { }
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccessConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = TTypeValidationState;
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter()) and
|
||||
none()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
access.isTypeNotValidated() and
|
||||
none()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("lock_input") and
|
||||
fc.getArgument(0) = node1.asExpr() and
|
||||
fc = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
// leave this predicate as-is for this workshop step
|
||||
none()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) {
|
||||
// leave this predicate as-is for this workshop step
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
module InputToAccess = DataFlow::MakeWithState<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
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."
|
||||
117
exercises/Exercise14.ql
Normal file
117
exercises/Exercise14.ql
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState {
|
||||
TypeValidationCall call;
|
||||
DataFlow::Node node1;
|
||||
DataFlow::Node node2;
|
||||
|
||||
TypeValidatedState() { none() }
|
||||
|
||||
/**
|
||||
* Returns the call to `DYN_INPUT_TYPE` that validated the type of the dynamic input array.
|
||||
*/
|
||||
TypeValidationCall getCall() { result = call }
|
||||
|
||||
/**
|
||||
* Returns the node *from* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getFstNode() { result = node1 }
|
||||
|
||||
/**
|
||||
* Returns the node *to* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getSndNode() { result = node2 }
|
||||
}
|
||||
|
||||
from TypeValidatedState state, DataFlow::Node preceding
|
||||
where DataFlow::localFlowStep(preceding, state.getFstNode())
|
||||
select state.getFstNode(), state.getSndNode(), state.getCall()
|
||||
200
exercises/Exercise15.ql
Normal file
200
exercises/Exercise15.ql
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* @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"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
/**
|
||||
* Returns the integer value of the expected type specified for the element at `input_index`
|
||||
*/
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState {
|
||||
TypeValidationCall call;
|
||||
DataFlow::Node node1;
|
||||
DataFlow::Node node2;
|
||||
|
||||
TypeValidatedState() {
|
||||
this = TTypeValidatedState(call) and
|
||||
exists(GuardCondition gc |
|
||||
DataFlow::localFlowStep(node1, node2) and
|
||||
typeValidationGuard(gc, call, _, node2.asExpr().getBasicBlock()) and
|
||||
not typeValidationGuard(gc, call, _, node1.asExpr().getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call to `DYN_INPUT_TYPE` that validated the type of the dynamic input array.
|
||||
*/
|
||||
TypeValidationCall getCall() { result = call }
|
||||
|
||||
/**
|
||||
* Returns the node *from* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getFstNode() { result = node1 }
|
||||
|
||||
/**
|
||||
* Returns the node *to* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getSndNode() { result = node2 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccessConfig {
|
||||
class FlowState = TTypeValidationState;
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter()) and
|
||||
state instanceof TypeUnvalidatedState
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
none()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("lock_input") and
|
||||
fc.getArgument(0) = node1.asExpr() and
|
||||
fc = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) { none() }
|
||||
}
|
||||
|
||||
module InputToAccess = DataFlow::MakeWithState<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
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."
|
||||
227
exercises/Exercise16.ql
Normal file
227
exercises/Exercise16.ql
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* @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"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
/**
|
||||
* Returns the integer value of the expected type specified for the element at `input_index`
|
||||
*/
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState {
|
||||
TypeValidationCall call;
|
||||
DataFlow::Node node1;
|
||||
DataFlow::Node node2;
|
||||
|
||||
TypeValidatedState() {
|
||||
this = TTypeValidatedState(call) and
|
||||
exists(GuardCondition gc |
|
||||
DataFlow::localFlowStep(node1, node2) and
|
||||
typeValidationGuard(gc, call, _, node2.asExpr().getBasicBlock()) and
|
||||
not typeValidationGuard(gc, call, _, node1.asExpr().getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call to `DYN_INPUT_TYPE` that validated the type of the dynamic input array.
|
||||
*/
|
||||
TypeValidationCall getCall() { result = call }
|
||||
|
||||
/**
|
||||
* Returns the node *from* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getFstNode() { result = node1 }
|
||||
|
||||
/**
|
||||
* Returns the node *to* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getSndNode() { result = node2 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccessConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = TTypeValidationState;
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter()) and
|
||||
state instanceof TypeUnvalidatedState
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
(
|
||||
state instanceof TypeUnvalidatedState
|
||||
or
|
||||
state instanceof TypeValidatedState and
|
||||
not typeValidationCallMatchesUse(state.(TypeValidatedState).getCall(), access)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("lock_input") and
|
||||
fc.getArgument(0) = node1.asExpr() and
|
||||
fc = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
state1 instanceof TypeUnvalidatedState and
|
||||
node1 = state2.(TypeValidatedState).getFstNode() and
|
||||
node2 = state2.(TypeValidatedState).getSndNode()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) {
|
||||
exists(TypeValidationCall call |
|
||||
typeValidationGuard(_, call, _, node.asExpr().getBasicBlock()) and
|
||||
(
|
||||
state instanceof TypeUnvalidatedState
|
||||
or
|
||||
state.(TypeValidatedState).getCall() != call
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module InputToAccess = DataFlow::MakeWithState<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
from
|
||||
InputToAccess::PathNode source, InputToAccess::PathNode sink, string message, Expr additionalExpr
|
||||
where
|
||||
InputToAccess::hasFlowPath(source, sink) and
|
||||
(
|
||||
if none()
|
||||
then (
|
||||
message = "No type validation performed before dynamic input access." and
|
||||
none()
|
||||
) else (
|
||||
message = "Dynamic input access does not match prior type-validation $@." and
|
||||
none()
|
||||
)
|
||||
)
|
||||
select sink, source, sink, message, additionalExpr, "here"
|
||||
@@ -81,7 +81,9 @@ module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DynamicInputAccess access | sink.asExpr() = access.getArrayBase())
|
||||
exists(DynamicInputAccess access | sink.asExpr() = access.getArrayBase() and
|
||||
not typeValidationGuard(_, _, _, access.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
|
||||
186
solutions/Exercise13.ql
Normal file
186
solutions/Exercise13.ql
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @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") }
|
||||
|
||||
/**
|
||||
* Returns the integer value of the expected type specified for the element at `input_index`
|
||||
*/
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState { }
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccessConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = TTypeValidationState;
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter()) and
|
||||
state instanceof TypeUnvalidatedState
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
access.isTypeNotValidated() and
|
||||
state instanceof TypeUnvalidatedState
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("lock_input") and
|
||||
fc.getArgument(0) = node1.asExpr() and
|
||||
fc = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
// leave this predicate as-is for this workshop step
|
||||
none()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) {
|
||||
// leave this predicate as-is for this workshop step
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
module InputToAccess = DataFlow::MakeWithState<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
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."
|
||||
125
solutions/Exercise14.ql
Normal file
125
solutions/Exercise14.ql
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @id cpp/check-type-before-use
|
||||
* @name Check type before use
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 getInputTypesParameter() { result = this.getParameter(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState {
|
||||
TypeValidationCall call;
|
||||
DataFlow::Node node1;
|
||||
DataFlow::Node node2;
|
||||
BasicBlock bb2;
|
||||
|
||||
TypeValidatedState() {
|
||||
this = TTypeValidatedState(call) and
|
||||
exists(GuardCondition gc |
|
||||
DataFlow::localFlowStep(node1, node2) and
|
||||
typeValidationGuard(gc, call, _, node2.asExpr().getBasicBlock()) and
|
||||
not typeValidationGuard(gc, call, _, node1.asExpr().getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call to `DYN_INPUT_TYPE` that validated the type of the dynamic input array.
|
||||
*/
|
||||
TypeValidationCall getCall() { result = call }
|
||||
|
||||
/**
|
||||
* Returns the node *from* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getFstNode() { result = node1 }
|
||||
|
||||
/**
|
||||
* Returns the node *to* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getSndNode() { result = node2 }
|
||||
}
|
||||
|
||||
from TypeValidatedState state, DataFlow::Node preceding
|
||||
where DataFlow::localFlowStep(preceding, state.getFstNode())
|
||||
select state.getFstNode(), state.getSndNode(), state.getCall()
|
||||
216
solutions/Exercise15.ql
Normal file
216
solutions/Exercise15.ql
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* @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"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
/**
|
||||
* Returns the integer value of the expected type specified for the element at `input_index`
|
||||
*/
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState {
|
||||
TypeValidationCall call;
|
||||
DataFlow::Node node1;
|
||||
DataFlow::Node node2;
|
||||
|
||||
TypeValidatedState() {
|
||||
this = TTypeValidatedState(call) and
|
||||
exists(GuardCondition gc |
|
||||
DataFlow::localFlowStep(node1, node2) and
|
||||
typeValidationGuard(gc, call, _, node2.asExpr().getBasicBlock()) and
|
||||
not typeValidationGuard(gc, call, _, node1.asExpr().getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call to `DYN_INPUT_TYPE` that validated the type of the dynamic input array.
|
||||
*/
|
||||
TypeValidationCall getCall() { result = call }
|
||||
|
||||
/**
|
||||
* Returns the node *from* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getFstNode() { result = node1 }
|
||||
|
||||
/**
|
||||
* Returns the node *to* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getSndNode() { result = node2 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccessConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = TTypeValidationState;
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter()) and
|
||||
state instanceof TypeUnvalidatedState
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
(
|
||||
state instanceof TypeUnvalidatedState
|
||||
or
|
||||
state instanceof TypeValidatedState and
|
||||
not typeValidationCallMatchesUse(state.(TypeValidatedState).getCall(), access)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("lock_input") and
|
||||
fc.getArgument(0) = node1.asExpr() and
|
||||
fc = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
state1 instanceof TypeUnvalidatedState and
|
||||
node1 = state2.(TypeValidatedState).getFstNode() and
|
||||
node2 = state2.(TypeValidatedState).getSndNode()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) {
|
||||
exists(TypeValidationCall call |
|
||||
typeValidationGuard(_, call, _, node.asExpr().getBasicBlock()) and
|
||||
(
|
||||
state instanceof TypeUnvalidatedState
|
||||
or
|
||||
state.(TypeValidatedState).getCall() != call
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module InputToAccess = DataFlow::MakeWithState<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
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."
|
||||
227
solutions/Exercise16.ql
Normal file
227
solutions/Exercise16.ql
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* @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"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `DYN_INPUT_TYPE`
|
||||
*/
|
||||
class TypeValidationCall extends FunctionCall {
|
||||
TypeValidationCall() { this.getTarget().hasName("DYN_INPUT_TYPE") }
|
||||
|
||||
/**
|
||||
* Returns the integer value of the expected type specified for the element at `input_index`
|
||||
*/
|
||||
int getExpectedInputType(int input_index) {
|
||||
result = this.getArgument(input_index).getValue().toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guard.ensuresEq(dest, other, 0, 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 flow-state representing the state of dynamic input type validation.
|
||||
*/
|
||||
newtype TTypeValidationState =
|
||||
TTypeUnvalidatedState() or
|
||||
TTypeValidatedState(TypeValidationCall call)
|
||||
|
||||
/**
|
||||
* A class describing the type validation state
|
||||
*/
|
||||
class TypeValidationState extends TTypeValidationState {
|
||||
string toString() {
|
||||
this = TTypeUnvalidatedState() and result = "unvalidated"
|
||||
or
|
||||
this = TTypeValidatedState(_) and result = "validated"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has *not* been validated.
|
||||
*/
|
||||
class TypeUnvalidatedState extends TypeValidationState, TTypeUnvalidatedState { }
|
||||
|
||||
/**
|
||||
* A flow-state in which the type of a dynamic input array has been validated.
|
||||
*/
|
||||
class TypeValidatedState extends TypeValidationState, TTypeValidatedState {
|
||||
TypeValidationCall call;
|
||||
DataFlow::Node node1;
|
||||
DataFlow::Node node2;
|
||||
|
||||
TypeValidatedState() {
|
||||
this = TTypeValidatedState(call) and
|
||||
exists(GuardCondition gc |
|
||||
DataFlow::localFlowStep(node1, node2) and
|
||||
typeValidationGuard(gc, call, _, node2.asExpr().getBasicBlock()) and
|
||||
not typeValidationGuard(gc, call, _, node1.asExpr().getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call to `DYN_INPUT_TYPE` that validated the type of the dynamic input array.
|
||||
*/
|
||||
TypeValidationCall getCall() { result = call }
|
||||
|
||||
/**
|
||||
* Returns the node *from* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getFstNode() { result = node1 }
|
||||
|
||||
/**
|
||||
* Returns the node *to* which the state-changing step occurs
|
||||
*/
|
||||
DataFlow::Node getSndNode() { result = node2 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A global data-flow configuration tracking flow from an entrypoint's
|
||||
* `input` parameter to a subsequent `ArrayExpr` access.
|
||||
*/
|
||||
module InputToAccessConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState = TTypeValidationState;
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(EntrypointFunction ep | ep.getInputParameter() = source.asParameter()) and
|
||||
state instanceof TypeUnvalidatedState
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(DynamicInputAccess access |
|
||||
sink.asExpr() = access.getArrayBase() and
|
||||
(
|
||||
state instanceof TypeUnvalidatedState
|
||||
or
|
||||
state instanceof TypeValidatedState and
|
||||
not typeValidationCallMatchesUse(state.(TypeValidatedState).getCall(), access)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("lock_input") and
|
||||
fc.getArgument(0) = node1.asExpr() and
|
||||
fc = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
state1 instanceof TypeUnvalidatedState and
|
||||
node1 = state2.(TypeValidatedState).getFstNode() and
|
||||
node2 = state2.(TypeValidatedState).getSndNode()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) {
|
||||
exists(TypeValidationCall call |
|
||||
typeValidationGuard(_, call, _, node.asExpr().getBasicBlock()) and
|
||||
(
|
||||
state instanceof TypeUnvalidatedState
|
||||
or
|
||||
state.(TypeValidatedState).getCall() != call
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module InputToAccess = DataFlow::MakeWithState<InputToAccessConfig>;
|
||||
|
||||
import InputToAccess::PathGraph
|
||||
|
||||
from
|
||||
InputToAccess::PathNode source, InputToAccess::PathNode sink, string message, Expr additionalExpr
|
||||
where
|
||||
InputToAccess::hasFlowPath(source, sink) and
|
||||
(
|
||||
if sink.getState() instanceof TypeUnvalidatedState
|
||||
then (
|
||||
message = "No type validation performed before dynamic input access." and
|
||||
additionalExpr = sink.getNode().asExpr()
|
||||
) else (
|
||||
message = "Dynamic input access does not match prior type-validation $@." and
|
||||
additionalExpr = sink.getState().(TypeValidatedState).getCall()
|
||||
)
|
||||
)
|
||||
select sink, source, sink, message, additionalExpr, "here"
|
||||
@@ -81,7 +81,9 @@ module InputToAccessConfig implements DataFlow::ConfigSig {
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(DynamicInputAccess access | sink.asExpr() = access.getArrayBase())
|
||||
exists(DynamicInputAccess access | sink.asExpr() = access.getArrayBase() and
|
||||
not typeValidationGuard(_, _, _, access.getBasicBlock())
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
|
||||
Reference in New Issue
Block a user