mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
C++: Introduce a node class that exists before computing SSA.
This commit is contained in:
@@ -6,6 +6,111 @@ private import DataFlowImplConsistency
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TIRDataFlowNode0 =
|
||||
TInstructionNode0(Instruction i) {
|
||||
not Ssa::ignoreInstruction(i) and
|
||||
// We exclude `void`-typed instructions because they cannot contain data.
|
||||
// However, if the instruction is a glvalue, and their type is `void`, then the result
|
||||
// type of the instruction is really `void*`, and thus we still want to have a dataflow
|
||||
// node for it.
|
||||
(not i.getResultType() instanceof VoidType or i.isGLValue())
|
||||
} or
|
||||
TOperandNode0(Operand op) { not Ssa::ignoreOperand(op) }
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
class Node0Impl extends TIRDataFlowNode0 {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Declaration getEnclosingCallable() { none() } // overridden in subclasses
|
||||
|
||||
/** Gets the function to which this node belongs, if any. */
|
||||
Declaration getFunction() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* Gets the type of this node.
|
||||
*
|
||||
* If `asInstruction().isGLValue()` holds, then the type of this node
|
||||
* should be thought of as "pointer to `getType()`".
|
||||
*/
|
||||
DataFlowType getType() { none() } // overridden in subclasses
|
||||
|
||||
/** Gets the instruction corresponding to this node, if any. */
|
||||
Instruction asInstruction() { result = this.(InstructionNode0).getInstruction() }
|
||||
|
||||
/** Gets the operands corresponding to this node, if any. */
|
||||
Operand asOperand() { result = this.(OperandNode0).getOperand() }
|
||||
|
||||
/** INTERNAL: Do not use. */
|
||||
Location getLocationImpl() {
|
||||
none() // overridden by subclasses
|
||||
}
|
||||
|
||||
/** INTERNAL: Do not use. */
|
||||
string toStringImpl() {
|
||||
none() // overridden by subclasses
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
final Location getLocation() { result = this.getLocationImpl() }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
final string toString() { result = this.toStringImpl() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class InstructionNode0 extends Node0Impl, TInstructionNode0 {
|
||||
Instruction instr;
|
||||
|
||||
InstructionNode0() { this = TInstructionNode0(instr) }
|
||||
|
||||
/** Gets the instruction corresponding to this node. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = instr.getEnclosingFunction() }
|
||||
|
||||
override DataFlowType getType() { result = instr.getResultType() }
|
||||
|
||||
final override Location getLocationImpl() { result = instr.getLocation() }
|
||||
|
||||
override string toStringImpl() {
|
||||
// This predicate is overridden in subclasses. This default implementation
|
||||
// does not use `Instruction.toString` because that's expensive to compute.
|
||||
result = this.getInstruction().getOpcode().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class OperandNode0 extends Node0Impl, TOperandNode0 {
|
||||
Operand op;
|
||||
|
||||
OperandNode0() { this = TOperandNode0(op) }
|
||||
|
||||
/** Gets the operand corresponding to this node. */
|
||||
Operand getOperand() { result = op }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = op.getUse().getEnclosingFunction() }
|
||||
|
||||
override DataFlowType getType() { result = op.getType() }
|
||||
|
||||
final override Location getLocationImpl() { result = op.getLocation() }
|
||||
|
||||
override string toStringImpl() { result = this.getOperand().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
@@ -251,7 +356,7 @@ class ReturnNode extends Node instanceof IndirectReturnNode {
|
||||
*/
|
||||
private predicate hasNonInitializeParameterDef(IRVariable v) {
|
||||
exists(Ssa::Def def |
|
||||
not def.getDefiningInstruction() instanceof InitializeParameterInstruction and
|
||||
not def.getValue().asInstruction() instanceof InitializeParameterInstruction and
|
||||
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,15 +33,7 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
newtype TIRDataFlowNode =
|
||||
TInstructionNode(Instruction i) {
|
||||
not Ssa::ignoreInstruction(i) and
|
||||
// We exclude `void`-typed instructions because they cannot contain data.
|
||||
// However, if the instruction is a glvalue, and their type is `void`, then the result
|
||||
// type of the instruction is really `void*`, and thus we still want to have a dataflow
|
||||
// node for it.
|
||||
(not i.getResultType() instanceof VoidType or i.isGLValue())
|
||||
} or
|
||||
TOperandNode(Operand op) { not Ssa::ignoreOperand(op) } or
|
||||
TNode0(Node0Impl node) or
|
||||
TVariableNode(Variable var) or
|
||||
TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) {
|
||||
indirectionIndex =
|
||||
@@ -335,52 +327,50 @@ private string toExprString(Node n) {
|
||||
result = n.asIndirectExpr().toString() + " indirection"
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class InstructionNode extends Node, TInstructionNode {
|
||||
Instruction instr;
|
||||
class Node0 extends Node, TNode0 {
|
||||
Node0Impl node;
|
||||
|
||||
InstructionNode() { this = TInstructionNode(instr) }
|
||||
Node0() { this = TNode0(node) }
|
||||
|
||||
/** Gets the instruction corresponding to this node. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
override Declaration getEnclosingCallable() { result = node.getEnclosingCallable() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
override Declaration getFunction() { result = node.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = instr.getEnclosingFunction() }
|
||||
override DataFlowType getType() { result = node.getType() }
|
||||
|
||||
override DataFlowType getType() { result = instr.getResultType() }
|
||||
|
||||
final override Location getLocationImpl() { result = instr.getLocation() }
|
||||
final override Location getLocationImpl() { result = node.getLocationImpl() }
|
||||
|
||||
override string toStringImpl() {
|
||||
// This predicate is overridden in subclasses. This default implementation
|
||||
// does not use `Instruction.toString` because that's expensive to compute.
|
||||
result = this.getInstruction().getOpcode().toString()
|
||||
result = node.toStringImpl()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class InstructionNode extends Node0 {
|
||||
override InstructionNode0 node;
|
||||
Instruction instr;
|
||||
|
||||
InstructionNode() { instr = node.getInstruction() }
|
||||
|
||||
/** Gets the instruction corresponding to this node. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class OperandNode extends Node, TOperandNode {
|
||||
class OperandNode extends Node, Node0 {
|
||||
override OperandNode0 node;
|
||||
Operand op;
|
||||
|
||||
OperandNode() { this = TOperandNode(op) }
|
||||
OperandNode() { op = node.getOperand() }
|
||||
|
||||
/** Gets the operand corresponding to this node. */
|
||||
Operand getOperand() { result = op }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = op.getUse().getEnclosingFunction() }
|
||||
|
||||
override DataFlowType getType() { result = op.getType() }
|
||||
|
||||
final override Location getLocationImpl() { result = op.getLocation() }
|
||||
|
||||
override string toStringImpl() { result = this.getOperand().toString() }
|
||||
Operand getOperand() { result = node.getOperand() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -681,7 +671,7 @@ class UninitializedNode extends Node {
|
||||
|
||||
UninitializedNode() {
|
||||
exists(Ssa::Def def |
|
||||
def.getDefiningInstruction() instanceof UninitializedInstruction and
|
||||
def.getValue().asInstruction() instanceof UninitializedInstruction and
|
||||
Ssa::nodeToDefOrUse(this, def) and
|
||||
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
|
||||
)
|
||||
|
||||
@@ -222,16 +222,16 @@ class DefImpl extends DefOrUseImpl, TDefImpl {
|
||||
|
||||
override int getIndirectionIndex() { result = ind }
|
||||
|
||||
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
|
||||
Node0Impl getValue() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override string toString() { result = "DefImpl" }
|
||||
|
||||
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
|
||||
override IRBlock getBlock() { result = this.getAddressOperand().getUse().getBlock() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
|
||||
override Cpp::Location getLocation() { result = this.getAddressOperand().getUse().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getDefiningInstruction() = block.getInstruction(index)
|
||||
this.getAddressOperand().getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
predicate isCertain() { isDef(true, _, address, _, _, ind) }
|
||||
@@ -308,7 +308,10 @@ predicate outNodeHasAddressAndIndex(
|
||||
}
|
||||
|
||||
private predicate defToNode(Node nodeFrom, Def def) {
|
||||
nodeHasInstruction(nodeFrom, def.getDefiningInstruction(), def.getIndirectionIndex())
|
||||
// TODO: This is not yet needed (and in fact, the compiler rejects it because `exists(def.getValue().asOperand())` never holds).
|
||||
// nodeHasOperand(nodeFrom, def.getValue().asOperand(), def.getIndirectionIndex())
|
||||
// or
|
||||
nodeHasInstruction(nodeFrom, def.getValue().asInstruction(), def.getIndirectionIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,7 +565,7 @@ class Def extends DefOrUse {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirectionIndex()
|
||||
}
|
||||
|
||||
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
|
||||
Node0Impl getValue() { result = defOrUse.getValue() }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
@@ -4,6 +4,7 @@ import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
@@ -126,15 +127,29 @@ class AllocationInstruction extends CallInstruction {
|
||||
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i` is a base instruction that starts a sequence of uses
|
||||
* of some variable that SSA can handle.
|
||||
*
|
||||
* This is either when `i` is a `VariableAddressInstruction` or when
|
||||
* `i` is a fresh allocation produced by an `AllocationInstruction`.
|
||||
*/
|
||||
private predicate isSourceVariableBase(Instruction i) {
|
||||
i instanceof VariableAddressInstruction or i instanceof AllocationInstruction
|
||||
predicate isWrite(Node0Impl value, Operand address, boolean certain) {
|
||||
certain = true and
|
||||
(
|
||||
exists(StoreInstruction store |
|
||||
value.asInstruction() = store and
|
||||
address = store.getDestinationAddressOperand()
|
||||
)
|
||||
or
|
||||
exists(InitializeParameterInstruction init |
|
||||
value.asInstruction() = init and
|
||||
address = init.getAnOperand()
|
||||
)
|
||||
or
|
||||
exists(InitializeDynamicAllocationInstruction init |
|
||||
value.asInstruction() = init and
|
||||
address = init.getAllocationAddressOperand()
|
||||
)
|
||||
or
|
||||
exists(UninitializedInstruction uninitialized |
|
||||
value.asInstruction() = uninitialized and
|
||||
address = uninitialized.getAnOperand()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,6 +278,17 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i` is a base instruction that starts a sequence of uses
|
||||
* of some variable that SSA can handle.
|
||||
*
|
||||
* This is either when `i` is a `VariableAddressInstruction` or when
|
||||
* `i` is a fresh allocation produced by an `AllocationInstruction`.
|
||||
*/
|
||||
private predicate isSourceVariableBase(Instruction i) {
|
||||
i instanceof VariableAddressInstruction or i instanceof AllocationInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is an address of an SSA variable rooted at `base`,
|
||||
* and `instr` is a definition of the SSA variable with `ind` number of indirections.
|
||||
@@ -273,25 +299,17 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate isDef(
|
||||
boolean certain, Instruction instr, Operand address, Instruction base, int ind,
|
||||
boolean certain, Node0Impl value, Operand address, Instruction base, int ind,
|
||||
int indirectionIndex
|
||||
) {
|
||||
certain = true and
|
||||
exists(int ind0, CppType type, int lower, int upper |
|
||||
address =
|
||||
[
|
||||
instr.(StoreInstruction).getDestinationAddressOperand(),
|
||||
instr.(InitializeParameterInstruction).getAnOperand(),
|
||||
instr.(InitializeDynamicAllocationInstruction).getAllocationAddressOperand(),
|
||||
instr.(UninitializedInstruction).getAnOperand()
|
||||
]
|
||||
|
|
||||
isWrite(value, address, certain) and
|
||||
isDefImpl(address, base, ind0) and
|
||||
type = getLanguageType(address) and
|
||||
upper = countIndirectionsForCppType(type) and
|
||||
ind = ind0 + [lower .. upper] and
|
||||
indirectionIndex = ind - (ind0 + lower) and
|
||||
if type.hasType(any(Cpp::ArrayType arrayType), true) then lower = 0 else lower = 1
|
||||
(if type.hasType(any(Cpp::ArrayType arrayType), true) then lower = 0 else lower = 1)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -168,16 +168,16 @@ class DefImpl extends DefOrUseImpl, TDefImpl {
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
|
||||
Node0Impl getValue() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override string toString() { result = address.toString() }
|
||||
|
||||
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
|
||||
override IRBlock getBlock() { result = this.getAddressOperand().getDef().getBlock() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
|
||||
override Cpp::Location getLocation() { result = this.getAddressOperand().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getDefiningInstruction() = block.getInstruction(index)
|
||||
this.getAddressOperand().getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
predicate isCertain() { isDef(true, _, address, _, _, _) }
|
||||
@@ -302,9 +302,9 @@ class Def extends DefOrUse {
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
|
||||
Node0Impl getValue() { result = defOrUse.getValue() }
|
||||
|
||||
override string toString() { result = this.asDefOrUse().toString() + " (def)" }
|
||||
override string toString() { result = this.asDefOrUse().toString() }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
Reference in New Issue
Block a user