Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Node0Impl extends TIRDataFlowNode0 {
/**
* Gets the type of this node.
*
* If `asInstruction().isGLValue()` holds, then the type of this node
* If `isGLValue()` holds, then the type of this node
* should be thought of as "pointer to `getType()`".
*/
DataFlowType getType() { none() } // overridden in subclasses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,14 @@ class SsaPhiNode extends Node, TSsaPhiNode {

override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }

override DataFlowType getType() { result = this.getAnInput().getType().getUnspecifiedType() }
override DataFlowType getType() {
exists(Ssa::SourceVariable sv |
this.getPhiNode().definesAt(sv, _, _, _) and
result = sv.getType()
)
}

override predicate isGLValue() { phi.getSourceVariable().isGLValue() }

final override Location getLocationImpl() { result = phi.getBasicBlock().getLocation() }

Expand Down
76 changes: 53 additions & 23 deletions cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ private module SourceVariables {
* indirections) of this source variable.
*/
abstract BaseSourceVariable getBaseVariable();

/** Holds if this variable is a glvalue. */
predicate isGLValue() { none() }

/**
* Gets the type of this source variable. If `isGLValue()` holds, then
* the type of this source variable should be thought of as "pointer
* to `getType()`".
*/
abstract DataFlowType getType();
}

class SourceIRVariable extends SourceVariable, TSourceIRVariable {
Expand All @@ -66,6 +76,12 @@ private module SourceVariables {
ind > 0 and
result = this.getIRVariable().toString() + " indirection"
}

override predicate isGLValue() { ind = 0 }

override DataFlowType getType() {
if ind = 0 then result = var.getType() else result = getTypeImpl(var.getType(), ind - 1)
}
}

class CallVariable extends SourceVariable, TCallVariable {
Expand All @@ -84,6 +100,8 @@ private module SourceVariables {
ind > 0 and
result = "Call indirection"
}

override DataFlowType getType() { result = getTypeImpl(call.getResultType(), ind) }
}
}

Expand Down Expand Up @@ -530,15 +548,15 @@ predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
exists(IRBlock bb1, int i1, SourceVariable v |
defOrUse1.asDefOrUse().hasIndexInBlock(bb1, i1, v)
|
exists(IRBlock bb2, int i2, Definition def |
adjacentDefRead(pragma[only_bind_into](def), pragma[only_bind_into](bb1),
exists(IRBlock bb2, int i2, DefinitionExt def |
adjacentDefReadExt(pragma[only_bind_into](def), pragma[only_bind_into](bb1),
pragma[only_bind_into](i1), pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) and
def.getSourceVariable() = v and
use.asDefOrUse().(UseImpl).hasIndexInBlock(bb2, i2, v)
)
or
exists(PhiNode phi |
lastRefRedef(_, bb1, i1, phi) and
lastRefRedefExt(_, bb1, i1, phi) and
use.asPhi() = phi and
phi.getSourceVariable() = pragma[only_bind_into](v)
)
Expand All @@ -550,11 +568,18 @@ predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
* flows to `useOrPhi`.
*/
private predicate globalDefToUse(GlobalDef globalDef, UseOrPhi useOrPhi) {
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, SourceVariable v |
globalDef.hasIndexInBlock(bb1, i1, v) and
adjacentDefRead(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) and
useOrPhi.asDefOrUse().hasIndexInBlock(bb2, i2, v)
exists(IRBlock bb1, int i1, SourceVariable v | globalDef.hasIndexInBlock(bb1, i1, v) |
exists(IRBlock bb2, int i2 |
adjacentDefReadExt(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) and
useOrPhi.asDefOrUse().hasIndexInBlock(bb2, i2, v)
)
or
exists(PhiNode phi |
lastRefRedefExt(_, bb1, i1, phi) and
useOrPhi.asPhi() = phi and
phi.getSourceVariable() = pragma[only_bind_into](v)
Comment on lines +578 to +581
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR mostly looks sensible. This is the only part I have trouble wrapping my head around. What does this do?

Copy link
Contributor Author

@MathiasVP MathiasVP Mar 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lastRefRedefExt(_, bb1, i1, phi) says that the last reference to the variable defined by phi was at index i1 in basic block bb1. This means that (bb1, i1) is an input to phi. Does that help?

It's basically the same pattern as here. We should probably factor that out into a common predicate in another PR.

)
)
}

Expand Down Expand Up @@ -666,17 +691,17 @@ private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo,
* Holds if `def` is the corresponding definition of
* the SSA library's `definition`.
*/
private Definition ssaDefinition(Def def) {
private DefinitionExt ssaDefinition(Def def) {
exists(IRBlock block, int i, SourceVariable sv |
def.hasIndexInBlock(block, i, sv) and
result.definesAt(sv, block, i)
result.definesAt(sv, block, i, _)
)
}

/** Gets a node that represents the prior definition of `node`. */
private Node getAPriorDefinition(SsaDefOrUse defOrUse) {
exists(IRBlock bb, int i, SourceVariable sv, Definition def, DefOrUse defOrUse0 |
SsaCached::lastRefRedef(pragma[only_bind_into](def), pragma[only_bind_into](bb),
exists(IRBlock bb, int i, SourceVariable sv, DefinitionExt def, DefOrUse defOrUse0 |
lastRefRedefExt(pragma[only_bind_into](def), pragma[only_bind_into](bb),
pragma[only_bind_into](i), ssaDefinition(defOrUse)) and
def.getSourceVariable() = sv and
defOrUse0.hasIndexInBlock(bb, i, sv) and
Expand All @@ -702,7 +727,7 @@ pragma[nomagic]
private predicate fromPhiNodeToUse(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, UseOrPhi use) {
exists(IRBlock bb2, int i2 |
use.asDefOrUse().hasIndexInBlock(bb2, i2, sv) and
adjacentDefRead(pragma[only_bind_into](phi), pragma[only_bind_into](bb1),
adjacentDefReadExt(pragma[only_bind_into](phi), pragma[only_bind_into](bb1),
pragma[only_bind_into](i1), pragma[only_bind_into](bb2), pragma[only_bind_into](i2))
)
}
Expand All @@ -711,13 +736,13 @@ private predicate fromPhiNodeToUse(PhiNode phi, SourceVariable sv, IRBlock bb1,
predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, UseOrPhi use |
phi = nodeFrom.getPhiNode() and
phi.definesAt(sv, bb1, i1) and
phi.definesAt(sv, bb1, i1, _) and
useToNode(use, nodeTo)
|
fromPhiNodeToUse(phi, sv, bb1, i1, use)
or
exists(PhiNode phiTo |
lastRefRedef(phi, _, _, phiTo) and
lastRefRedefExt(phi, _, _, phiTo) and
nodeTo.(SsaPhiNode).getPhiNode() = phiTo
)
)
Expand Down Expand Up @@ -796,8 +821,8 @@ module SsaCached {
* path between them without any read of `def`.
*/
cached
predicate adjacentDefRead(Definition def, IRBlock bb1, int i1, IRBlock bb2, int i2) {
SsaImpl::adjacentDefRead(def, bb1, i1, bb2, i2)
predicate adjacentDefReadExt(DefinitionExt def, IRBlock bb1, int i1, IRBlock bb2, int i2) {
SsaImpl::adjacentDefReadExt(def, _, bb1, i1, bb2, i2)
}

/**
Expand All @@ -806,8 +831,8 @@ module SsaCached {
* without passing through another read or write.
*/
cached
predicate lastRefRedef(Definition def, IRBlock bb, int i, Definition next) {
SsaImpl::lastRefRedef(def, bb, i, next)
predicate lastRefRedefExt(DefinitionExt def, IRBlock bb, int i, DefinitionExt next) {
SsaImpl::lastRefRedefExt(def, _, bb, i, next)
}
}

Expand All @@ -818,8 +843,8 @@ private newtype TSsaDefOrUse =
or
// Like in the pruning stage, we only include definition that's live after the
// write as the final definitions computed by SSA.
exists(Definition def, SourceVariable sv, IRBlock bb, int i |
def.definesAt(sv, bb, i) and
exists(DefinitionExt def, SourceVariable sv, IRBlock bb, int i |
def.definesAt(sv, bb, i, _) and
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
)
} or
Expand Down Expand Up @@ -967,9 +992,14 @@ class Def extends DefOrUse {

private module SsaImpl = SsaImplCommon::Make<SsaInput>;

class PhiNode = SsaImpl::PhiNode;
class PhiNode extends SsaImpl::DefinitionExt {
PhiNode() {
this instanceof SsaImpl::PhiNode or
this instanceof SsaImpl::PhiReadNode
}
}

class Definition = SsaImpl::Definition;
class DefinitionExt = SsaImpl::DefinitionExt;

class UncertainWriteDefinition = SsaImpl::UncertainWriteDefinition;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,8 @@ private newtype TSsaDefOrUse =
or
// If `defOrUse` is a definition we only include it if the
// SSA library concludes that it's live after the write.
exists(Definition def, SourceVariable sv, IRBlock bb, int i |
def.definesAt(sv, bb, i) and
exists(DefinitionExt def, SourceVariable sv, IRBlock bb, int i |
def.definesAt(sv, bb, i, _) and
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
)
} or
Expand Down Expand Up @@ -301,6 +301,11 @@ class Def extends DefOrUse {

private module SsaImpl = SsaImplCommon::Make<SsaInput>;

class PhiNode = SsaImpl::PhiNode;
class PhiNode extends SsaImpl::DefinitionExt {
PhiNode() {
this instanceof SsaImpl::PhiNode or
this instanceof SsaImpl::PhiReadNode
}
}

class Definition = SsaImpl::Definition;
class DefinitionExt = SsaImpl::DefinitionExt;
13 changes: 13 additions & 0 deletions cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,3 +615,16 @@ void test_flow_through_void_double_pointer(int *p) // $ ast-def=p
void* q = (void*)&p;
sink(**(int**)q); // $ ir MISSING: ast
}

void use(int *);

void test_def_via_phi_read(bool b)
{
static int buffer[10]; // This is missing an initialisation in IR dataflow
if (b)
{
use(buffer);
}
intPointerSource(buffer);
sink(buffer); // $ ast,ir
}