Skip to content

C++: Add an interprocedural control-flow library #149

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 11 additions & 0 deletions cpp/ql/lib/semmle/code/cpp/interproccontrolflow/ControlFlow.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import cpp

/**
* Provides classes for performing global (inter-procedural) control flow analyses.
*/
module ControlFlow {
private import internal.ControlFlowSpecific
private import shared.ControlFlow
import ControlFlowMake<Location, CppControlFlow>
import Public
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
private import semmle.code.cpp.ir.IR
private import cpp as Cpp
private import ControlFlowPublic
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon

predicate edge(Node n1, Node n2) { n1.asInstruction().getASuccessor() = n2.asInstruction() }

predicate callTarget(CallNode call, Callable target) {
exists(DataFlowPrivate::DataFlowCall dfCall | dfCall.asCallInstruction() = call.asInstruction() |
DataFlowImplCommon::viableCallableCached(dfCall).asSourceCallable() = target
or
DataFlowImplCommon::viableCallableLambda(dfCall, _).asSourceCallable() = target
)
}

predicate flowEntry(Callable c, Node entry) {
entry.asInstruction().(EnterFunctionInstruction).getEnclosingFunction() = c
}

predicate flowExit(Callable c, Node exitNode) {
exitNode.asInstruction().(ExitFunctionInstruction).getEnclosingFunction() = c
}

Callable getEnclosingCallable(Node n) { n.getEnclosingFunction() = result }

predicate hiddenNode(Node n) { n.asInstruction() instanceof PhiInstruction }

private newtype TSplit = TNone() { none() }

class Split extends TSplit {
abstract string toString();

abstract Cpp::Location getLocation();

abstract predicate entry(Node n1, Node n2);

abstract predicate exit(Node n1, Node n2);

abstract predicate blocked(Node n1, Node n2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
private import semmle.code.cpp.ir.IR
private import cpp

private newtype TNode = TInstructionNode(Instruction i)

abstract private class NodeImpl extends TNode {
/** Gets the `Instruction` associated with this node, if any. */
Instruction asInstruction() { result = this.(InstructionNode).getInstruction() }

/** Gets the `Expr` associated with this node, if any. */
Expr asExpr() { result = this.(ExprNode).getExpr() }

/** Gets the `Parameter` associated with this node, if any. */
Parameter asParameter() { result = this.(ParameterNode).getParameter() }

/** Gets the location of this node. */
Location getLocation() { none() }

/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
final predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}

/** Gets a textual representation of this node. */
abstract string toString();

/** Gets the enclosing callable of this node. */
abstract Callable getEnclosingFunction();
}

final class Node = NodeImpl;

private class InstructionNode extends NodeImpl {
Instruction instr;

InstructionNode() { this = TInstructionNode(instr) }

/** Gets the `Instruction` associated with this node. */
Instruction getInstruction() { result = instr }

final override Location getLocation() { result = instr.getLocation() }

final override string toString() { result = instr.getAst().toString() }

final override Callable getEnclosingFunction() { result = instr.getEnclosingFunction() }
}

private class ExprNode extends InstructionNode {
Expr e;

ExprNode() { e = this.getInstruction().getConvertedResultExpression() }

/** Gets the `Expr` associated with this node. */
Expr getExpr() { result = e }
}

private class ParameterNode extends InstructionNode {
override InitializeParameterInstruction instr;
Parameter p;

ParameterNode() { p = instr.getParameter() }

/** Gets the `Parameter` associated with this node. */
Parameter getParameter() { result = p }
}

class CallNode extends InstructionNode {
override CallInstruction instr;
}

class Callable = Function;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Provides IR-specific definitions for use in the data flow library.
*/

private import cpp
private import semmle.code.cpp.interproccontrolflow.shared.ControlFlow

module Private {
import ControlFlowPrivate
}

module Public {
import ControlFlowPublic
}

module CppControlFlow implements InputSig<Location> {
import Private
import Public
}
140 changes: 140 additions & 0 deletions cpp/ql/lib/semmle/code/cpp/interproccontrolflow/shared/ControlFlow.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
private import codeql.util.Location

/** Provides language-specific control flow parameters. */
signature module InputSig<LocationSig Location> {
/**
* A node in the control flow graph.
*/
class Node {
/** Gets a textual representation of this element. */
string toString();

/** Gets the location of this node. */
Location getLocation();
}

class CallNode extends Node;

class Callable;

predicate edge(Node n1, Node n2);

predicate callTarget(CallNode call, Callable target);

predicate flowEntry(Callable c, Node entry);

predicate flowExit(Callable c, Node exitNode);

Callable getEnclosingCallable(Node n);

predicate hiddenNode(Node n);

class Split {
string toString();

Location getLocation();

predicate entry(Node n1, Node n2);

predicate exit(Node n1, Node n2);

predicate blocked(Node n1, Node n2);
}
}

private module Configs<LocationSig Location, InputSig<Location> Lang> {
private import Lang

/** An input configuration for control flow. */
signature module ConfigSig {
/** Holds if `source` is a relevant control flow source. */
predicate isSource(Node src);

/** Holds if `sink` is a relevant control flow sink. */
predicate isSink(Node sink);

/** Holds if control flow should not proceed along the edge `n1 -> n2`. */
default predicate isBarrierEdge(Node n1, Node n2) { none() }

/**
* Holds if control flow through `node` is prohibited. This completely
* removes `node` from the control flow graph.
*/
default predicate isBarrier(Node n) { none() }
}

/** An input configuration for control flow using a label. */
signature module LabelConfigSig {
class Label;

/**
* Holds if `source` is a relevant control flow source with the given
* initial `l`.
*/
predicate isSource(Node src, Label l);

/**
* Holds if `sink` is a relevant control flow sink accepting `l`.
*/
predicate isSink(Node sink, Label l);

/**
* Holds if control flow should not proceed along the edge `n1 -> n2` when
* the label is `l`.
*/
default predicate isBarrierEdge(Node n1, Node n2, Label l) { none() }

/**
* Holds if control flow through `node` is prohibited when the label
* is `l`.
*/
default predicate isBarrier(Node n, Label l) { none() }
}
}

module ControlFlowMake<LocationSig Location, InputSig<Location> Lang> {
private import Lang
private import internal.ControlFlowImpl::MakeImpl<Location, Lang>
import Configs<Location, Lang>

/**
* The output of a global control flow computation.
*/
signature module GlobalFlowSig {
/**
* A `Node` that is reachable from a source, and which can reach a sink.
*/
class PathNode;

/**
* Holds if control can flow from `source` to `sink`.
*
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate flowPath(PathNode source, PathNode sink);
}

/**
* Constructs a global control flow computation.
*/
module Global<ConfigSig Config> implements GlobalFlowSig {
private module C implements FullConfigSig {
import DefaultLabel<Config>
import Config
}

import Impl<C>
}

/**
* Constructs a global control flow computation using a flow label.
*/
module GlobalWithLabel<LabelConfigSig Config> implements GlobalFlowSig {
private module C implements FullConfigSig {
import Config
}

import Impl<C>
}
}
Loading