C++: Add an 'asDefinition' overload to check if a definition is certain or not.

This commit is contained in:
Mathias Vorreiter Pedersen
2025-03-12 17:04:06 +00:00
parent 40903a9643
commit 66e8b2d7e5

View File

@@ -313,13 +313,79 @@ class Node extends TIRDataFlowNode {
* `n.asExpr() instanceof IncrementOperation` since the result of evaluating
* the expression `x++` is passed to `sink`.
*/
Expr asDefinition() {
exists(StoreInstruction store |
Expr asDefinition() { result = this.asDefinition(_) }
/**
* Gets the definition associated with this node, if any.
*
* For example, consider the following example
* ```cpp
* int x = 42; // 1
* x = 34; // 2
* ++x; // 3
* x++; // 4
* x += 1; // 5
* int y = x += 2; // 6
* ```
* - For (1) the result is `42`.
* - For (2) the result is `x = 34`.
* - For (3) the result is `++x`.
* - For (4) the result is `x++`.
* - For (5) the result is `x += 1`.
* - For (6) there are two results:
* - For the definition generated by `x += 2` the result is `x += 2`
* - For the definition generated by `int y = ...` the result is
* also `x += 2`.
*
* For assignments, `node.asDefinition()` and `node.asExpr()` will both exist
* for the same dataflow node. However, for expression such as `x++` that
* both write to `x` and read the current value of `x`, `node.asDefinition()`
* will give the node corresponding to the value after the increment, and
* `node.asExpr()` will give the node corresponding to the value before the
* increment. For an example of this, consider the following:
*
* ```cpp
* sink(x++);
* ```
* in the above program, there will not be flow from a node `n` such that
* `n.asDefinition() instanceof IncrementOperation` to the argument of `sink`
* since the value passed to `sink` is the value before to the increment.
* However, there will be dataflow from a node `n` such that
* `n.asExpr() instanceof IncrementOperation` since the result of evaluating
* the expression `x++` is passed to `sink`.
*
* If `uncertain = false` then the definition is guaranteed to overwrite
* the entire buffer pointed to by the destination address of the definition.
* Otherwise, `uncertain = true`.
*
* For example, the write `int x; x = 42;` is guaranteed to overwrite all the
* bytes allocated to `x`, while the assignment `int p[10]; p[3] = 42;` has
* `uncertain = true` since the write will not overwrite the entire buffer
* pointed to by `p`.
*/
Expr asDefinition(boolean uncertain) {
exists(StoreInstruction store, Ssa::DefinitionExt def |
store = this.asInstruction() and
result = asDefinitionImpl(store)
result = asDefinitionImpl(store) and
Ssa::defToNode(this, def, _, _, _, _) and
if def.isCertain() then uncertain = false else uncertain = true
)
}
/**
* Gets the definition associated with this node, if this node is a certain definition.
*
* See `Node.asDefinition` for a description of certain and uncertain definitions.
*/
Expr asCertainDefinition() { result = this.asDefinition(false) }
/**
* Gets the definition associated with this node, if this node is an uncertain definition.
*
* See `Node.asDefinition` for a description of certain and uncertain definitions.
*/
Expr asUncertainDefinition() { result = this.asDefinition(true) }
/**
* Gets the indirect definition at a given indirection corresponding to this
* node, if any.