diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 07015db1c08..d92cda95105 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -260,6 +260,54 @@ class Node extends TIRDataFlowNode { */ Expr asDefiningArgument() { result = this.asDefiningArgument(_) } + /** + * 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` + */ + Expr asDefinition() { + exists(StoreInstruction store | + store = this.asInstruction() and + result = asDefinitionImpl(store) + ) + } + + /** + * Gets the indirect definition at a given indirection corresponding to this + * node, if any. + * + * See the comments on `Node.asDefinition` for examples. + */ + Expr asIndirectDefinition(int indirectionIndex) { + exists(StoreInstruction store | + this.(IndirectInstruction).hasInstructionAndIndirectionIndex(store, indirectionIndex) and + result = asDefinitionImpl(store) + ) + } + + /** + * Gets the indirect definition at some indirection corresponding to this + * node, if any. + */ + Expr asIndirectDefinition() { result = this.asIndirectDefinition(_) } + /** * Gets the argument that defines this `DefinitionByReferenceNode`, if any. * @@ -1142,22 +1190,6 @@ private module GetConvertedResultExpression { } private Expr getConvertedResultExpressionImpl0(Instruction instr) { - // For an expression such as `i += 2` we pretend that the generated - // `StoreInstruction` contains the result of the expression even though - // this isn't totally aligned with the C/C++ standard. - exists(TranslatedAssignOperation tao | - result = tao.getExpr() and - instr = tao.getInstruction(any(AssignmentStoreTag tag)) - ) - or - // Similarly for `i++` and `++i` we pretend that the generated - // `StoreInstruction` is contains the result of the expression even though - // this isn't totally aligned with the C/C++ standard. - exists(TranslatedCrementOperation tco | - result = tco.getExpr() and - instr = tco.getInstruction(any(CrementStoreTag tag)) - ) - or // IR construction inserts an additional cast to a `size_t` on the extent // of a `new[]` expression. The resulting `ConvertInstruction` doesn't have // a result for `getConvertedResultExpression`. We remap this here so that @@ -1182,6 +1214,75 @@ private module GetConvertedResultExpression { not exists(getConvertedResultExpressionImpl0(instr)) and result = instr.getConvertedResultExpression() } + + /** + * Gets the result for `node.asDefinition()` (when `node` is the instruction + * node that wraps `store`) in the cases where `store.getAst()` should not be + * used to define the result of `node.asDefinition()`. + */ + private Expr asDefinitionImpl0(StoreInstruction store) { + // For an expression such as `i += 2` we pretend that the generated + // `StoreInstruction` contains the result of the expression even though + // this isn't totally aligned with the C/C++ standard. + exists(TranslatedAssignOperation tao | + store = tao.getInstruction(any(AssignmentStoreTag tag)) and + result = tao.getExpr() + ) + or + // Similarly for `i++` and `++i` we pretend that the generated + // `StoreInstruction` is contains the result of the expression even though + // this isn't totally aligned with the C/C++ standard. + exists(TranslatedCrementOperation tco | + store = tco.getInstruction(any(CrementStoreTag tag)) and + result = tco.getExpr() + ) + } + + /** + * Holds if the expression returned by `store.getAst()` should not be + * returned as the result of `node.asDefinition()` when `node` is the + * instruction node that wraps `store`. + */ + private predicate excludeAsDefinitionResult(StoreInstruction store) { + // Exclude the store to the temporary generated by a ternary expression. + exists(TranslatedConditionalExpr tce | + store = tce.getInstruction(any(ConditionValueFalseStoreTag tag)) + or + store = tce.getInstruction(any(ConditionValueTrueStoreTag tag)) + ) + } + + /** + * Gets the expression that represents the result of `StoreInstruction` for + * dataflow purposes. + * + * 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 `StoreInstruction` generated by `x += 2` the result + * is `x += 2` + * - For the `StoreInstruction` generated by `int y = ...` the result + * is also `x += 2` + */ + Expr asDefinitionImpl(StoreInstruction store) { + not exists(asDefinitionImpl0(store)) and + not excludeAsDefinitionResult(store) and + result = store.getAst().(Expr).getUnconverted() + or + result = asDefinitionImpl0(store) + } } private import GetConvertedResultExpression @@ -1947,7 +2048,12 @@ module ExprFlowCached { isIndirectBaseOfArrayAccess(n, result) or not isIndirectBaseOfArrayAccess(n, _) and - result = n.asExpr() + ( + result = n.asExpr() + or + result = n.asDefinition() and + result instanceof CrementOperation + ) } /**