mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge branch 'main' into henrymercer/rc-3.11-mergeback
This commit is contained in:
@@ -271,13 +271,3 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
/**
|
||||
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
|
||||
*
|
||||
* This is a temporary hook to support technical debt in the Go language; do not use.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
any()
|
||||
}
|
||||
|
||||
@@ -297,6 +297,10 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,6 +297,10 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,6 +297,10 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,6 +297,10 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -18,4 +18,6 @@ module CppDataFlow implements InputSig {
|
||||
import Public
|
||||
|
||||
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
|
||||
|
||||
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
|
||||
}
|
||||
|
||||
@@ -804,6 +804,8 @@ predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
|
||||
|
||||
predicate localMustFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
|
||||
@@ -193,13 +193,23 @@ class Node extends TIRDataFlowNode {
|
||||
* a `Conversion`, then the result is the underlying non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
Expr asExpr() { result = this.asExpr(_) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr asExpr(int n) { result = this.(ExprNode).getExpr(n) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr asIndirectExpr(int n, int index) { result = this.(IndirectExprNode).getExpr(n, index) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression that's indirectly tracked by this node
|
||||
* under `index` number of indirections.
|
||||
*/
|
||||
Expr asIndirectExpr(int index) { result = this.(IndirectExprNode).getExpr(index) }
|
||||
Expr asIndirectExpr(int index) { result = this.asIndirectExpr(_, index) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression that's indirectly tracked by this node
|
||||
@@ -211,15 +221,26 @@ class Node extends TIRDataFlowNode {
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr asConvertedExpr() { result = this.(ExprNode).getConvertedExpr() }
|
||||
Expr asConvertedExpr() { result = this.asConvertedExpr(_) }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr asConvertedExpr(int n) { result = this.(ExprNode).getConvertedExpr(n) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr asIndirectConvertedExpr(int n, int index) {
|
||||
result = this.(IndirectExprNode).getConvertedExpr(n, index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that's indirectly tracked by this node
|
||||
* behind `index` number of indirections.
|
||||
*/
|
||||
Expr asIndirectConvertedExpr(int index) {
|
||||
result = this.(IndirectExprNode).getConvertedExpr(index)
|
||||
}
|
||||
Expr asIndirectConvertedExpr(int index) { result = this.asIndirectConvertedExpr(_, index) }
|
||||
|
||||
/**
|
||||
* Gets the expression that's indirectly tracked by this node behind a
|
||||
@@ -254,9 +275,7 @@ class Node extends TIRDataFlowNode {
|
||||
* after the `f` has returned.
|
||||
*/
|
||||
Expr asDefiningArgument(int index) {
|
||||
// Subtract one because `DefinitionByReferenceNode` is defined to be in
|
||||
// the range `[0 ... n - 1]` for some `n` instead of `[1 ... n]`.
|
||||
this.(DefinitionByReferenceNode).getIndirectionIndex() = index - 1 and
|
||||
this.(DefinitionByReferenceNode).getIndirectionIndex() = index and
|
||||
result = this.(DefinitionByReferenceNode).getArgument()
|
||||
}
|
||||
|
||||
@@ -393,9 +412,10 @@ class Node extends TIRDataFlowNode {
|
||||
}
|
||||
|
||||
private string toExprString(Node n) {
|
||||
result = n.asExpr().toString()
|
||||
result = n.asExpr(0).toString()
|
||||
or
|
||||
result = n.asIndirectExpr().toString() + " indirection"
|
||||
not exists(n.asExpr()) and
|
||||
result = n.asIndirectExpr(0, 1).toString() + " indirection"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -935,7 +955,7 @@ class RawIndirectOperand extends Node, TRawIndirectOperand {
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = instructionNode(this.getOperand().getDef()).toStringImpl() + " indirection"
|
||||
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,77 +1062,130 @@ class RawIndirectInstruction extends Node, TRawIndirectInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e) {
|
||||
exists(Instruction def |
|
||||
unique( | | getAUse(def)) = node.getOperand() and
|
||||
e = def.getConvertedResultExpression()
|
||||
)
|
||||
private module GetConvertedResultExpression {
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
|
||||
private Operand getAnInitializeDynamicAllocationInstructionAddress() {
|
||||
result = any(InitializeDynamicAllocationInstruction init).getAllocationAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that should be returned as the result expression from `instr`.
|
||||
*
|
||||
* Note that this predicate may return multiple results in cases where a conversion belongs to a
|
||||
* different AST element than its operand.
|
||||
*/
|
||||
Expr getConvertedResultExpression(Instruction instr, int n) {
|
||||
// Only fully converted instructions have a result for `asConvertedExpr`
|
||||
not conversionFlow(unique(Operand op |
|
||||
// The address operand of a `InitializeDynamicAllocationInstruction` is
|
||||
// special: we need to handle it during dataflow (since it's
|
||||
// effectively a store to an indirection), but it doesn't appear in
|
||||
// source syntax, so dataflow node <-> expression conversion shouldn't
|
||||
// care about it.
|
||||
op = getAUse(instr) and not op = getAnInitializeDynamicAllocationInstructionAddress()
|
||||
|
|
||||
op
|
||||
), _, false, false) and
|
||||
result = getConvertedResultExpressionImpl(instr) and
|
||||
n = 0
|
||||
or
|
||||
// If the conversion also has a result then we return multiple results
|
||||
exists(Operand operand | conversionFlow(operand, instr, false, false) |
|
||||
n = 1 and
|
||||
result = getConvertedResultExpressionImpl(operand.getDef())
|
||||
or
|
||||
result = getConvertedResultExpression(operand.getDef(), n - 1)
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
// this `ConvertInstruction` maps to the result of the expression that
|
||||
// represents the extent.
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
result = tas.getExtent().getExpr() and
|
||||
instr = tas.getInstruction(any(AllocationExtentConvertTag tag))
|
||||
)
|
||||
or
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
// expect this
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr().(ParenthesisExpr) and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl(Instruction instr) {
|
||||
result = getConvertedResultExpressionImpl0(instr)
|
||||
or
|
||||
not exists(getConvertedResultExpressionImpl0(instr)) and
|
||||
result = instr.getConvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand0(
|
||||
VariableAddressInstruction instr, RawIndirectOperand node, Expr e
|
||||
) {
|
||||
instr = node.getOperand().getDef() and
|
||||
e = instr.getAst().(Expr).getUnconverted()
|
||||
private import GetConvertedResultExpression
|
||||
|
||||
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
||||
exists(Instruction def |
|
||||
unique( | | getAUse(def)) = node.getOperand() and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectOperand` that maps `node.asIndirectExpr()` to `e`. */
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand(RawIndirectOperand node, Expr e) {
|
||||
exists(Instruction instr | instr = node.getOperand().getDef() |
|
||||
exists(Expr e0 |
|
||||
indirectExprNodeShouldBeIndirectOperand0(instr, node, e0) and
|
||||
e = e0.getFullyConverted()
|
||||
)
|
||||
or
|
||||
not indirectExprNodeShouldBeIndirectOperand0(_, node, _) and
|
||||
e = instr.getConvertedResultExpression()
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand(
|
||||
IndirectOperand node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
exists(Instruction def |
|
||||
node.hasOperandAndIndirectionIndex(unique( | | getAUse(def)), indirectionIndex) and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e) {
|
||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() instanceof Constructor and
|
||||
e = call.getConvertedResultExpression() and
|
||||
e = getConvertedResultExpression(call, n) and
|
||||
call.getThisArgumentOperand() = node.getAddressOperand()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an instruction node that maps `node.asExpr()` to `e`. */
|
||||
predicate exprNodeShouldBeInstruction(Node node, Expr e) {
|
||||
not exprNodeShouldBeOperand(_, e) and
|
||||
not exprNodeShouldBeIndirectOutNode(_, e) and
|
||||
(
|
||||
e = node.asInstruction().getConvertedResultExpression()
|
||||
or
|
||||
// The instruction that contains the result of an `AssignOperation` is
|
||||
// the unloaded left operand (see the comments in `TranslatedAssignOperation`).
|
||||
// That means that for cases like
|
||||
// ```cpp
|
||||
// int x = ...;
|
||||
// x += 1;
|
||||
// ```
|
||||
// the result of `x += 1` is the `VariableAddressInstruction` that represents `x`. But
|
||||
// that instruction doesn't receive the flow from this `AssignOperation`. So instead we
|
||||
// map the operation to the `AddInstruction`.
|
||||
node.asInstruction().getAst() = e.(AssignOperation)
|
||||
or
|
||||
// Same story for `CrementOperation`s (cf. the comments in the subclasses
|
||||
// of `TranslatedCrementOperation`).
|
||||
node.asInstruction().getAst() = e.(CrementOperation)
|
||||
)
|
||||
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
||||
not exprNodeShouldBeOperand(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
||||
e = getConvertedResultExpression(node.asInstruction(), n)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
|
||||
predicate indirectExprNodeShouldBeIndirectInstruction(IndirectInstruction node, Expr e) {
|
||||
predicate indirectExprNodeShouldBeIndirectInstruction(
|
||||
IndirectInstruction node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e, n, indirectionIndex) and
|
||||
exists(Instruction instr |
|
||||
node.hasInstructionAndIndirectionIndex(instr, _) and
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e)
|
||||
|
|
||||
e = instr.(VariableAddressInstruction).getAst().(Expr).getFullyConverted()
|
||||
or
|
||||
not instr instanceof VariableAddressInstruction and
|
||||
e = instr.getConvertedResultExpression()
|
||||
node.hasInstructionAndIndirectionIndex(instr, indirectionIndex) and
|
||||
e = getConvertedResultExpression(instr, n)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1121,30 +1194,32 @@ abstract private class ExprNodeBase extends Node {
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr();
|
||||
abstract Expr getConvertedExpr(int n);
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
abstract Expr getExpr();
|
||||
final Expr getExpr(int n) { result = this.getConvertedExpr(n).getUnconverted() }
|
||||
}
|
||||
|
||||
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
||||
InstructionExprNode() { exprNodeShouldBeInstruction(this, _) }
|
||||
InstructionExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeInstruction(this, e, n) and
|
||||
not exprNodeShouldBeInstruction(_, e, n + 1)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr() { exprNodeShouldBeInstruction(this, result) }
|
||||
|
||||
final override Expr getExpr() { result = this.getConvertedExpr().getUnconverted() }
|
||||
|
||||
final override string toStringImpl() { result = this.getConvertedExpr().toString() }
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeInstruction(this, result, n) }
|
||||
}
|
||||
|
||||
private class OperandExprNode extends ExprNodeBase, OperandNode {
|
||||
OperandExprNode() { exprNodeShouldBeOperand(this, _) }
|
||||
OperandExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeOperand(this, e, n) and
|
||||
not exprNodeShouldBeOperand(_, e, n + 1)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr() { exprNodeShouldBeOperand(this, result) }
|
||||
|
||||
final override Expr getExpr() { result = this.getConvertedExpr().getUnconverted() }
|
||||
|
||||
final override string toStringImpl() { result = this.getConvertedExpr().toString() }
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeOperand(this, result, n) }
|
||||
}
|
||||
|
||||
abstract private class IndirectExprNodeBase extends Node {
|
||||
@@ -1152,67 +1227,75 @@ abstract private class IndirectExprNodeBase extends Node {
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr(int indirectionIndex);
|
||||
abstract Expr getConvertedExpr(int n, int indirectionIndex);
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
abstract Expr getExpr(int indirectionIndex);
|
||||
}
|
||||
|
||||
private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase, RawIndirectOperand {
|
||||
IndirectOperandIndirectExprNode() { indirectExprNodeShouldBeIndirectOperand(this, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
indirectExprNodeShouldBeIndirectOperand(this, result)
|
||||
}
|
||||
|
||||
final override Expr getExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
result = this.getConvertedExpr(index).getUnconverted()
|
||||
final Expr getExpr(int n, int indirectionIndex) {
|
||||
result = this.getConvertedExpr(n, indirectionIndex).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase,
|
||||
RawIndirectInstruction
|
||||
private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase instanceof IndirectOperand
|
||||
{
|
||||
IndirectInstructionIndirectExprNode() { indirectExprNodeShouldBeIndirectInstruction(this, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, result)
|
||||
IndirectOperandIndirectExprNode() {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectExprNodeShouldBeIndirectOperand(this, e, n, indirectionIndex) and
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e, n + 1, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
result = this.getConvertedExpr(index).getUnconverted()
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
indirectExprNodeShouldBeIndirectOperand(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase instanceof IndirectInstruction
|
||||
{
|
||||
IndirectInstructionIndirectExprNode() {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, e, n, indirectionIndex) and
|
||||
not indirectExprNodeShouldBeIndirectInstruction(_, e, n + 1, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectArgumentOutExprNode extends ExprNodeBase, IndirectArgumentOutNode {
|
||||
IndirectArgumentOutExprNode() { exprNodeShouldBeIndirectOutNode(this, _) }
|
||||
IndirectArgumentOutExprNode() { exprNodeShouldBeIndirectOutNode(this, _, _) }
|
||||
|
||||
final override Expr getConvertedExpr() { exprNodeShouldBeIndirectOutNode(this, result) }
|
||||
|
||||
final override Expr getExpr() { result = this.getConvertedExpr() }
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node instanceof ExprNodeBase {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getExpr(int n) { result = super.getExpr(n) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any. If
|
||||
* this node strictly (in the sense of `getConvertedExpr`) corresponds to a
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
Expr getExpr() { result = super.getExpr() }
|
||||
final Expr getExpr() { result = this.getExpr(_) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getConvertedExpr(int n) { result = super.getConvertedExpr(n) }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr getConvertedExpr() { result = super.getConvertedExpr() }
|
||||
final Expr getConvertedExpr() { result = this.getConvertedExpr(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1225,13 +1308,27 @@ class IndirectExprNode extends Node instanceof IndirectExprNodeBase {
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
Expr getExpr(int indirectionIndex) { result = super.getExpr(indirectionIndex) }
|
||||
final Expr getExpr(int indirectionIndex) { result = this.getExpr(_, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getExpr(int n, int indirectionIndex) { result = super.getExpr(n, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getConvertedExpr(int n, int indirectionIndex) {
|
||||
result = super.getConvertedExpr(n, indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr getConvertedExpr(int indirectionIndex) { result = super.getConvertedExpr(indirectionIndex) }
|
||||
Expr getConvertedExpr(int indirectionIndex) {
|
||||
result = this.getConvertedExpr(_, indirectionIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1257,6 +1354,9 @@ class ParameterNode extends Node {
|
||||
* pointer-indirection parameters are at further negative positions.
|
||||
*/
|
||||
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
|
||||
|
||||
/** Gets the `Parameter` associated with this node, if it exists. */
|
||||
Parameter getParameter() { none() } // overridden by subclasses
|
||||
}
|
||||
|
||||
/** An explicit positional parameter, including `this`, but not `...`. */
|
||||
@@ -1279,10 +1379,9 @@ private class ExplicitParameterNode extends ParameterNode, DirectParameterNode {
|
||||
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
|
||||
}
|
||||
|
||||
/** Gets the `Parameter` associated with this node. */
|
||||
Parameter getParameter() { result = instr.getParameter() }
|
||||
|
||||
override string toStringImpl() { result = instr.getParameter().toString() }
|
||||
|
||||
override Parameter getParameter() { result = instr.getParameter() }
|
||||
}
|
||||
|
||||
/** An implicit `this` parameter. */
|
||||
@@ -1444,7 +1543,7 @@ OperandNode operandNode(Operand operand) { result.getOperand() = operand }
|
||||
* _out of_ an expression, like when an argument is passed by reference, use
|
||||
* `definitionByReferenceNodeFromArgument` instead.
|
||||
*/
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
ExprNode exprNode(Expr e) { result.getExpr(_) = e }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may
|
||||
@@ -1452,7 +1551,7 @@ ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
* argument is passed by reference, use
|
||||
* `definitionByReferenceNodeFromArgument` instead.
|
||||
*/
|
||||
ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
|
||||
ExprNode convertedExprNode(Expr e) { result.getConvertedExpr(_) = e }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of `p` at function entry.
|
||||
|
||||
@@ -447,9 +447,16 @@ class GlobalUse extends UseImpl, TGlobalUse {
|
||||
IRFunction getIRFunction() { result = f }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
exists(ExitFunctionInstruction exit |
|
||||
exit = f.getExitFunctionInstruction() and
|
||||
block.getInstruction(index) = exit
|
||||
// Similar to the `FinalParameterUse` case, we want to generate flow out of
|
||||
// globals at any exit so that we can flow out of non-returning functions.
|
||||
// Obviously this isn't correct as we can't actually flow but the global flow
|
||||
// requires this if we want to flow into children.
|
||||
exists(Instruction return |
|
||||
return instanceof ReturnInstruction or
|
||||
return instanceof UnreachedInstruction
|
||||
|
|
||||
block.getInstruction(index) = return and
|
||||
return.getEnclosingIRFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
@@ -818,7 +825,7 @@ predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
|
||||
or
|
||||
exists(PhiNode phiTo |
|
||||
phi != phiTo and
|
||||
lastRefRedefExt(phi, _, _, phiTo) and
|
||||
lastRefRedefExt(phi, bb1, i1, phiTo) and
|
||||
nodeTo.(SsaPhiNode).getPhiNode() = phiTo
|
||||
)
|
||||
)
|
||||
|
||||
@@ -405,9 +405,6 @@ predicate hasUnreachedInstruction(IRFunction func) {
|
||||
exists(Call c |
|
||||
c.getEnclosingFunction() = func.getFunction() and
|
||||
any(Options opt).exits(c.getTarget())
|
||||
) and
|
||||
not exists(TranslatedUnreachableReturnStmt return |
|
||||
return.getEnclosingFunction().getFunction() = func.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -824,6 +824,9 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated Locatable getAST() { result = this.getAst() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = this.getAst().getLocation() }
|
||||
|
||||
/**
|
||||
* Get the first instruction to be executed in the evaluation of this element.
|
||||
*/
|
||||
|
||||
@@ -1906,8 +1906,10 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType()) and
|
||||
(
|
||||
this.extentNeedsConversion() and
|
||||
// Convert the extent to `size_t`, because the AST doesn't do this already.
|
||||
tag = AllocationExtentConvertTag() and opcode instanceof Opcode::Convert
|
||||
tag = AllocationExtentConvertTag() and
|
||||
opcode instanceof Opcode::Convert
|
||||
or
|
||||
tag = AllocationElementSizeTag() and opcode instanceof Opcode::Constant
|
||||
or
|
||||
@@ -1918,6 +1920,7 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
this.extentNeedsConversion() and
|
||||
tag = AllocationExtentConvertTag() and
|
||||
result = this.getInstruction(AllocationElementSizeTag())
|
||||
or
|
||||
@@ -1933,7 +1936,9 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getExtent() and
|
||||
result = this.getInstruction(AllocationExtentConvertTag())
|
||||
if this.extentNeedsConversion()
|
||||
then result = this.getInstruction(AllocationExtentConvertTag())
|
||||
else result = this.getInstruction(AllocationElementSizeTag())
|
||||
}
|
||||
|
||||
final override string getInstructionConstantValue(InstructionTag tag) {
|
||||
@@ -1945,19 +1950,31 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
tag = AllocationSizeTag() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
result = this.getInstruction(AllocationExtentConvertTag())
|
||||
(
|
||||
if this.extentNeedsConversion()
|
||||
then result = this.getInstruction(AllocationExtentConvertTag())
|
||||
else result = this.getExtent().getResult()
|
||||
)
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getInstruction(AllocationElementSizeTag())
|
||||
)
|
||||
or
|
||||
this.extentNeedsConversion() and
|
||||
tag = AllocationExtentConvertTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = this.getExtent().getResult()
|
||||
}
|
||||
|
||||
private TranslatedExpr getExtent() {
|
||||
result = getTranslatedExpr(expr.getExtent().getFullyConverted())
|
||||
TranslatedExpr getExtent() { result = getTranslatedExpr(expr.getExtent().getFullyConverted()) }
|
||||
|
||||
/**
|
||||
* Holds if the result of `expr.getExtent()` does not have the same type as
|
||||
* the allocator's size parameter.
|
||||
*/
|
||||
private predicate extentNeedsConversion() {
|
||||
expr.getExtent().getFullyConverted().getUnspecifiedType() !=
|
||||
expr.getAllocator().getParameter(0).getUnspecifiedType()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ class TranslatedStaticStorageDurationVarInit extends TranslatedRootElement,
|
||||
|
||||
final override Declaration getFunction() { result = var }
|
||||
|
||||
final Location getLocation() { result = var.getLocation() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getInstruction(EnterFunctionTag()) }
|
||||
|
||||
override TranslatedElement getChild(int n) {
|
||||
|
||||
@@ -442,29 +442,26 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
|
||||
|
||||
/**
|
||||
* The IR translation of an implicit `return` statement generated by the extractor to handle control
|
||||
* flow that reaches the end of a non-`void`-returning function body. Since such control flow
|
||||
* produces undefined behavior, we simply generate an `Unreached` instruction to prevent that flow
|
||||
* from continuing on to pollute other analysis. The assumption is that the developer is certain
|
||||
* that the implicit `return` is unreachable, even if the compiler cannot prove it.
|
||||
* flow that reaches the end of a non-`void`-returning function body. Such control flow
|
||||
* produces undefined behavior in C++ but not in C. However even in C using the return value is
|
||||
* undefined behaviour. We make it return uninitialized memory to get as much flow as possible.
|
||||
*/
|
||||
class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
|
||||
TranslatedUnreachableReturnStmt() {
|
||||
class TranslatedNoValueReturnStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
|
||||
TranslatedNoValueReturnStmt() {
|
||||
not stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction())
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::Unreached and
|
||||
resultType = getVoidType()
|
||||
final override Instruction getInitializationSuccessor() {
|
||||
result = this.getEnclosingFunction().getReturnSuccessorInstruction()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
final override Type getTargetType() { result = this.getEnclosingFunction().getReturnType() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
final override TranslatedInitialization getInitialization() { none() }
|
||||
|
||||
final override IRVariable getIRVariable() {
|
||||
result = this.getEnclosingFunction().getReturnVariable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,65 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
|
||||
or
|
||||
instr.getSuccessor(kind) instanceof UnreachedInstruction and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
isCallToNonReturningFunction(instr) and exists(instr.getSuccessor(kind))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all calls to `f` never return (e.g. they call `exit` or loop forever)
|
||||
*/
|
||||
private predicate isNonReturningFunction(IRFunction f) {
|
||||
// If the function has an instruction with a missing successor then
|
||||
// the analysis is probably going to be incorrect, so assume they exit.
|
||||
not hasInstructionWithMissingSuccessor(f) and
|
||||
(
|
||||
// If all flows to the exit block are pass through an unreachable then f never returns.
|
||||
any(UnreachedInstruction instr).getBlock().postDominates(f.getEntryBlock())
|
||||
or
|
||||
// If there is no flow to the exit block then f never returns.
|
||||
not exists(IRBlock entry, IRBlock exit |
|
||||
exit = f.getExitFunctionInstruction().getBlock() and
|
||||
entry = f.getEntryBlock() and
|
||||
exit = entry.getASuccessor*()
|
||||
)
|
||||
or
|
||||
// If all flows to the exit block are pass through a call that never returns then f never returns.
|
||||
exists(CallInstruction ci |
|
||||
ci.getBlock().postDominates(f.getEntryBlock()) and
|
||||
isCallToNonReturningFunction(ci)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has an instruction with a missing successor.
|
||||
* This matches `instructionWithoutSuccessor` from `IRConsistency`, but
|
||||
* avoids generating the error strings.
|
||||
*/
|
||||
predicate hasInstructionWithMissingSuccessor(IRFunction f) {
|
||||
exists(Instruction missingSucc |
|
||||
missingSucc.getEnclosingIRFunction() = f and
|
||||
not exists(missingSucc.getASuccessor()) and
|
||||
not missingSucc instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not missingSucc instanceof PhiInstruction and
|
||||
not missingSucc instanceof UnreachedInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call `ci` never returns.
|
||||
*/
|
||||
private predicate isCallToNonReturningFunction(CallInstruction ci) {
|
||||
exists(IRFunction callee, Language::Function staticTarget |
|
||||
staticTarget = ci.getStaticCallTarget() and
|
||||
staticTarget = callee.getFunction() and
|
||||
// We can't easily tell if the call is virtual or not
|
||||
// if the callee is virtual. So assume that the call is virtual
|
||||
// if the target is.
|
||||
not staticTarget.isVirtual() and
|
||||
isNonReturningFunction(callee)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
|
||||
@@ -10,6 +10,65 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
|
||||
or
|
||||
instr.getSuccessor(kind) instanceof UnreachedInstruction and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
isCallToNonReturningFunction(instr) and exists(instr.getSuccessor(kind))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all calls to `f` never return (e.g. they call `exit` or loop forever)
|
||||
*/
|
||||
private predicate isNonReturningFunction(IRFunction f) {
|
||||
// If the function has an instruction with a missing successor then
|
||||
// the analysis is probably going to be incorrect, so assume they exit.
|
||||
not hasInstructionWithMissingSuccessor(f) and
|
||||
(
|
||||
// If all flows to the exit block are pass through an unreachable then f never returns.
|
||||
any(UnreachedInstruction instr).getBlock().postDominates(f.getEntryBlock())
|
||||
or
|
||||
// If there is no flow to the exit block then f never returns.
|
||||
not exists(IRBlock entry, IRBlock exit |
|
||||
exit = f.getExitFunctionInstruction().getBlock() and
|
||||
entry = f.getEntryBlock() and
|
||||
exit = entry.getASuccessor*()
|
||||
)
|
||||
or
|
||||
// If all flows to the exit block are pass through a call that never returns then f never returns.
|
||||
exists(CallInstruction ci |
|
||||
ci.getBlock().postDominates(f.getEntryBlock()) and
|
||||
isCallToNonReturningFunction(ci)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has an instruction with a missing successor.
|
||||
* This matches `instructionWithoutSuccessor` from `IRConsistency`, but
|
||||
* avoids generating the error strings.
|
||||
*/
|
||||
predicate hasInstructionWithMissingSuccessor(IRFunction f) {
|
||||
exists(Instruction missingSucc |
|
||||
missingSucc.getEnclosingIRFunction() = f and
|
||||
not exists(missingSucc.getASuccessor()) and
|
||||
not missingSucc instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not missingSucc instanceof PhiInstruction and
|
||||
not missingSucc instanceof UnreachedInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call `ci` never returns.
|
||||
*/
|
||||
private predicate isCallToNonReturningFunction(CallInstruction ci) {
|
||||
exists(IRFunction callee, Language::Function staticTarget |
|
||||
staticTarget = ci.getStaticCallTarget() and
|
||||
staticTarget = callee.getFunction() and
|
||||
// We can't easily tell if the call is virtual or not
|
||||
// if the callee is virtual. So assume that the call is virtual
|
||||
// if the target is.
|
||||
not staticTarget.isVirtual() and
|
||||
isNonReturningFunction(callee)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
|
||||
Reference in New Issue
Block a user