Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++: Repair MustFlow library for use-use flow #11311

Merged
merged 5 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
C++: Repair the 'MustFlow' library.
  • Loading branch information
MathiasVP committed Nov 18, 2022
commit b748ed8f43921ceb833425f3d990730cd0c4e761
52 changes: 26 additions & 26 deletions cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

private import cpp
import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.IR

/**
Expand All @@ -25,18 +24,18 @@ abstract class MustFlowConfiguration extends string {
/**
* Holds if `source` is a relevant data flow source.
*/
abstract predicate isSource(DataFlow::Node source);
abstract predicate isSource(Instruction source);

/**
* Holds if `sink` is a relevant data flow sink.
*/
abstract predicate isSink(DataFlow::Node sink);
abstract predicate isSink(Operand sink);
jketema marked this conversation as resolved.
Show resolved Hide resolved

/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
*/
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }

/** Holds if this configuration allows flow from arguments to parameters. */
predicate allowInterproceduralFlow() { any() }
Expand All @@ -48,30 +47,30 @@ abstract class MustFlowConfiguration extends string {
* included in the module `PathGraph`.
*/
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
this.isSource(source.getNode()) and
this.isSource(source.getInstruction()) and
source.getASuccessor+() = sink
}
}

/** Holds if `node` flows from a source. */
pragma[nomagic]
private predicate flowsFromSource(DataFlow::Node node, MustFlowConfiguration config) {
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
config.isSource(node)
or
exists(DataFlow::Node mid |
exists(Instruction mid |
step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config))
)
}

/** Holds if `node` flows to a sink. */
pragma[nomagic]
private predicate flowsToSink(DataFlow::Node node, MustFlowConfiguration config) {
private predicate flowsToSink(Instruction node, MustFlowConfiguration config) {
flowsFromSource(node, pragma[only_bind_into](config)) and
(
config.isSink(node)
config.isSink(node.getAUse())
jketema marked this conversation as resolved.
Show resolved Hide resolved
or
exists(DataFlow::Node mid |
exists(Instruction mid |
step(node, mid, config) and
flowsToSink(mid, pragma[only_bind_into](config))
)
Expand Down Expand Up @@ -198,12 +197,13 @@ private module Cached {
}

cached
predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
instructionToOperandStep(nodeFrom.asInstruction(), nodeTo.asOperand())
or
flowThroughCallable(nodeFrom.asInstruction(), nodeTo.asInstruction())
predicate step(Instruction nodeFrom, Instruction nodeTo) {
exists(Operand mid |
instructionToOperandStep(nodeFrom, mid) and
operandToInstructionStep(mid, nodeTo)
jketema marked this conversation as resolved.
Show resolved Hide resolved
)
or
operandToInstructionStep(nodeFrom.asOperand(), nodeTo.asInstruction())
flowThroughCallable(nodeFrom, nodeTo)
}
}

Expand All @@ -213,12 +213,12 @@ private module Cached {
* way around.
*/
pragma[inline]
private Declaration getEnclosingCallable(DataFlow::Node n) {
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingCallable()
private IRFunction getEnclosingCallable(Instruction n) {
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction()
}

/** Holds if `nodeFrom` flows to `nodeTo`. */
private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowConfiguration config) {
private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) {
exists(config) and
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
(
Expand All @@ -227,45 +227,45 @@ private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowC
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
)
or
config.isAdditionalFlowStep(nodeFrom, nodeTo)
config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
}

private newtype TLocalPathNode =
MkLocalPathNode(DataFlow::Node n, MustFlowConfiguration config) {
MkLocalPathNode(Instruction n, MustFlowConfiguration config) {
flowsToSink(n, config) and
(
config.isSource(n)
or
exists(MustFlowPathNode mid | step(mid.getNode(), n, config))
exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config))
)
}

/** A `Node` that is in a path from a source to a sink. */
class MustFlowPathNode extends TLocalPathNode {
DataFlow::Node n;
Instruction n;

MustFlowPathNode() { this = MkLocalPathNode(n, _) }

/** Gets the underlying node. */
DataFlow::Node getNode() { result = n }
Instruction getInstruction() { result = n }

/** Gets a textual representation of this node. */
string toString() { result = n.toString() }
string toString() { result = n.getAst().toString() }

/** Gets the location of this element. */
Location getLocation() { result = n.getLocation() }

/** Gets a successor node, if any. */
MustFlowPathNode getASuccessor() {
step(this.getNode(), result.getNode(), this.getConfiguration())
step(this.getInstruction(), result.getInstruction(), this.getConfiguration())
}

/** Gets the associated configuration. */
MustFlowConfiguration getConfiguration() { this = MkLocalPathNode(_, result) }
}

private class MustFlowPathSink extends MustFlowPathNode {
MustFlowPathSink() { this.getConfiguration().isSink(this.getNode()) }
MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ predicate intentionallyReturnsStackPointer(Function f) {
class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" }

override predicate isSource(DataFlow::Node source) {
override predicate isSource(Instruction source) {
// Holds if `source` is a node that represents the use of a stack variable
exists(VariableAddressInstruction var, Function func |
var = source.asInstruction() and
func = var.getEnclosingFunction() and
var = source and
func = source.getEnclosingFunction() and
var.getAstVariable() instanceof StackVariable and
// Pointer-to-member types aren't properly handled in the dbscheme.
not var.getResultType() instanceof PointerToMemberType and
Expand All @@ -40,15 +40,15 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
)
}

override predicate isSink(DataFlow::Node sink) {
override predicate isSink(Operand sink) {
// Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in
// a `ReturnValueInstruction`.
// We use the `StoreInstruction` instead of the instruction that defines the
// `ReturnValueInstruction`'s source value operand because the former has better location information.
exists(StoreInstruction store |
store.getDestinationAddress().(VariableAddressInstruction).getIRVariable() instanceof
IRReturnVariable and
sink.asOperand() = store.getSourceValueOperand()
sink = store.getSourceValueOperand()
)
}

Expand Down Expand Up @@ -77,10 +77,10 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
* }
* ```
*/
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
node2.asInstruction().(FieldAddressInstruction).getObjectAddressOperand() = node1.asOperand()
override predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
node2.(FieldAddressInstruction).getObjectAddressOperand() = node1
or
node2.asInstruction().(PointerOffsetInstruction).getLeftOperand() = node1.asOperand()
node2.(PointerOffsetInstruction).getLeftOperand() = node1
}
}

Expand All @@ -89,6 +89,6 @@ from
ReturnStackAllocatedMemoryConfig conf
where
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
source.getNode().asInstruction() = var
select sink.getNode(), source, sink, "May return stack-allocated memory from $@.", var.getAst(),
var.getAst().toString()
source.getInstruction() = var
select sink.getInstruction(), source, sink, "May return stack-allocated memory from $@.",
var.getAst(), var.getAst().toString()
40 changes: 19 additions & 21 deletions cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.ql
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,35 @@ import PathGraph
class UnsafeUseOfThisConfig extends MustFlowConfiguration {
UnsafeUseOfThisConfig() { this = "UnsafeUseOfThisConfig" }

override predicate isSource(DataFlow::Node source) { isSource(source, _, _) }
override predicate isSource(Instruction source) { isSource(source, _, _) }

override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
override predicate isSink(Operand sink) { isSink(sink, _) }
}

/** Holds if `instr` is a `this` pointer used by the call instruction `call`. */
predicate isSink(DataFlow::Node sink, CallInstruction call) {
predicate isSink(Operand sink, CallInstruction call) {
Fixed Show fixed Hide fixed
exists(PureVirtualFunction func |
call.getStaticCallTarget() = func and
call.getThisArgument() = sink.asInstruction() and
call.getThisArgumentOperand() = sink and
// Weed out implicit calls to destructors of a base class
not func instanceof Destructor
)
}

/** Holds if `init` initializes the `this` pointer in class `c`. */
predicate isSource(DataFlow::Node source, string msg, Class c) {
exists(InitializeParameterInstruction init | init = source.asInstruction() |
(
exists(Constructor func |
not func instanceof CopyConstructor and
not func instanceof MoveConstructor and
func = init.getEnclosingFunction() and
msg = "construction"
)
or
init.getEnclosingFunction() instanceof Destructor and msg = "destruction"
) and
init.getIRVariable() instanceof IRThisVariable and
init.getEnclosingFunction().getDeclaringType() = c
)
predicate isSource(InitializeParameterInstruction source, string msg, Class c) {
Fixed Show fixed Hide fixed
(
exists(Constructor func |
not func instanceof CopyConstructor and
not func instanceof MoveConstructor and
func = source.getEnclosingFunction() and
msg = "construction"
)
or
source.getEnclosingFunction() instanceof Destructor and msg = "destruction"
) and
source.getIRVariable() instanceof IRThisVariable and
source.getEnclosingFunction().getDeclaringType() = c
}

/**
Expand All @@ -68,8 +66,8 @@ predicate flows(
) {
exists(UnsafeUseOfThisConfig conf |
conf.hasFlowPath(source, sink) and
isSource(source.getNode(), msg, sourceClass) and
isSink(sink.getNode(), call)
isSource(source.getInstruction(), msg, sourceClass) and
isSink(sink.getInstruction().getAUse(), call)
)
}

Expand Down