mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge tag 'codeql-cli/latest'
Compatible with the latest released version of the CodeQL CLI
This commit is contained in:
@@ -5,155 +5,35 @@
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Declaration
|
||||
import semmle.code.cpp.metrics.MetricFile
|
||||
private import codeql.util.FileSystem
|
||||
|
||||
private module Input implements InputSig {
|
||||
abstract class ContainerBase extends @container {
|
||||
abstract string getAbsolutePath();
|
||||
|
||||
ContainerBase getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
string toString() { result = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
class FolderBase extends ContainerBase, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
}
|
||||
|
||||
class FileBase extends ContainerBase, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
}
|
||||
|
||||
predicate hasSourceLocationPrefix = sourceLocationPrefix/1;
|
||||
}
|
||||
|
||||
private module Impl = Make<Input>;
|
||||
|
||||
/** A file or folder. */
|
||||
class Container extends Locatable, @container {
|
||||
/**
|
||||
* Gets the absolute, canonical path of this container, using forward slashes
|
||||
* as path separator.
|
||||
*
|
||||
* The path starts with a _root prefix_ followed by zero or more _path
|
||||
* segments_ separated by forward slashes.
|
||||
*
|
||||
* The root prefix is of one of the following forms:
|
||||
*
|
||||
* 1. A single forward slash `/` (Unix-style)
|
||||
* 2. An upper-case drive letter followed by a colon and a forward slash,
|
||||
* such as `C:/` (Windows-style)
|
||||
* 3. Two forward slashes, a computer name, and then another forward slash,
|
||||
* such as `//FileServer/` (UNC-style)
|
||||
*
|
||||
* Path segments are never empty (that is, absolute paths never contain two
|
||||
* contiguous slashes, except as part of a UNC-style root prefix). Also, path
|
||||
* segments never contain forward slashes, and no path segment is of the
|
||||
* form `.` (one dot) or `..` (two dots).
|
||||
*
|
||||
* Note that an absolute path never ends with a forward slash, except if it is
|
||||
* a bare root prefix, that is, the path has no path segments. A container
|
||||
* whose absolute path has no segments is always a `Folder`, not a `File`.
|
||||
*/
|
||||
string getAbsolutePath() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* Gets the relative path of this file or folder from the root folder of the
|
||||
* analyzed source location. The relative path of the root folder itself is
|
||||
* the empty string.
|
||||
*
|
||||
* This has no result if the container is outside the source root, that is,
|
||||
* if the root folder is not a reflexive, transitive parent of this container.
|
||||
*/
|
||||
string getRelativePath() {
|
||||
exists(string absPath, string pref |
|
||||
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
|
||||
|
|
||||
absPath = pref and result = ""
|
||||
or
|
||||
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
|
||||
not result.matches("/%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base name of this container including extension, that is, the last
|
||||
* segment of its absolute path, or the empty string if it has no segments.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding base names
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Base name</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst.js"</td></tr>
|
||||
* <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr>
|
||||
* <tr><td>"/"</td><td>""</td></tr>
|
||||
* <tr><td>"C:/"</td><td>""</td></tr>
|
||||
* <tr><td>"D:/"</td><td>""</td></tr>
|
||||
* <tr><td>"//FileServer/"</td><td>""</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getBaseName() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension of this container, that is, the suffix of its base name
|
||||
* after the last dot character, if any.
|
||||
*
|
||||
* In particular,
|
||||
*
|
||||
* - if the name does not include a dot, there is no extension, so this
|
||||
* predicate has no result;
|
||||
* - if the name ends in a dot, the extension is the empty string;
|
||||
* - if the name contains multiple dots, the extension follows the last dot.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding extensions
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Extension</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"js"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>not defined</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>""</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getExtension() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stem of this container, that is, the prefix of its base name up to
|
||||
* (but not including) the last dot character if there is one, or the entire
|
||||
* base name if there is not.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding stems
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Stem</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>""</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>"bash"</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getStem() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
|
||||
}
|
||||
|
||||
/** Gets the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/** Gets a file or sub-folder in this container. */
|
||||
Container getAChildContainer() { this = result.getParentContainer() }
|
||||
|
||||
/** Gets a file in this container. */
|
||||
File getAFile() { result = this.getAChildContainer() }
|
||||
|
||||
/** Gets the file in this container that has the given `baseName`, if any. */
|
||||
File getFile(string baseName) {
|
||||
result = this.getAFile() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/** Gets a sub-folder in this container. */
|
||||
Folder getAFolder() { result = this.getAChildContainer() }
|
||||
|
||||
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
|
||||
Folder getFolder(string baseName) {
|
||||
result = this.getAFolder() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of the path of this container.
|
||||
*
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
override string toString() { result = this.getAbsolutePath() }
|
||||
class Container extends Locatable, Impl::Container {
|
||||
override string toString() { result = Impl::Container.super.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,9 +46,7 @@ class Container extends Locatable, @container {
|
||||
*
|
||||
* To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
|
||||
class Folder extends Container, Impl::Folder {
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
@@ -189,9 +67,7 @@ class Folder extends Container, @folder {
|
||||
* The base name further decomposes into the _stem_ and _extension_ -- see
|
||||
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
|
||||
class File extends Container, Impl::File {
|
||||
override string getAPrimaryQlClass() { result = "File" }
|
||||
|
||||
override Location getLocation() {
|
||||
|
||||
@@ -73,6 +73,10 @@ private int isSource(Expr bufferExpr, Element why) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Same as `getBufferSize`, but with the `why` column projected away to prevent large duplications. */
|
||||
pragma[nomagic]
|
||||
int getBufferSizeProj(Expr bufferExpr) { result = getBufferSize(bufferExpr, _) }
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
|
||||
*/
|
||||
@@ -87,7 +91,7 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
why = bufferVar and
|
||||
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
|
||||
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
|
||||
result = getBufferSize(parentPtr, _) + bufferSize - parentClass.getSize()
|
||||
result = getBufferSizeProj(parentPtr) + bufferSize - parentClass.getSize()
|
||||
|
|
||||
if exists(bufferVar.getType().getSize())
|
||||
then bufferSize = bufferVar.getType().getSize()
|
||||
@@ -95,7 +99,6 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
)
|
||||
or
|
||||
// dataflow (all sources must be the same size)
|
||||
result = unique(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | getBufferSize(def, _)) and
|
||||
// find reason
|
||||
result = unique(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | getBufferSizeProj(def)) and
|
||||
exists(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | exists(getBufferSize(def, why)))
|
||||
}
|
||||
|
||||
@@ -26,17 +26,18 @@ predicate callDereferences(FunctionCall fc, int i) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if evaluation of `op` dereferences `e`.
|
||||
* Holds if evaluation of `op` dereferences `e` directly.
|
||||
*
|
||||
* This predicate does not recurse through function calls or arithmetic operations. To find
|
||||
* such cases, use `dereferencedByOperation`.
|
||||
*/
|
||||
predicate dereferencedByOperation(Expr op, Expr e) {
|
||||
predicate directDereferencedByOperation(Expr op, Expr e) {
|
||||
exists(PointerDereferenceExpr deref |
|
||||
deref.getAChild() = e and
|
||||
deref = op and
|
||||
not deref.getParent*() instanceof SizeofOperator
|
||||
)
|
||||
or
|
||||
exists(CrementOperation crement | dereferencedByOperation(e, op) and crement.getOperand() = e)
|
||||
or
|
||||
exists(ArrayExpr ae |
|
||||
(
|
||||
not ae.getParent() instanceof AddressOfExpr and
|
||||
@@ -50,6 +51,24 @@ predicate dereferencedByOperation(Expr op, Expr e) {
|
||||
)
|
||||
)
|
||||
or
|
||||
// ptr->Field
|
||||
e = op.(FieldAccess).getQualifier() and isClassPointerType(e.getType())
|
||||
or
|
||||
// ptr->method()
|
||||
e = op.(Call).getQualifier() and isClassPointerType(e.getType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if evaluation of `op` dereferences `e`.
|
||||
*
|
||||
* This includes the set of operations identified via `directDereferencedByOperation`, as well
|
||||
* as calls to function that are known to dereference an argument.
|
||||
*/
|
||||
predicate dereferencedByOperation(Expr op, Expr e) {
|
||||
directDereferencedByOperation(op, e)
|
||||
or
|
||||
exists(CrementOperation crement | dereferencedByOperation(e, op) and crement.getOperand() = e)
|
||||
or
|
||||
exists(AddressOfExpr addof, ArrayExpr ae |
|
||||
dereferencedByOperation(addof, op) and
|
||||
addof.getOperand() = ae and
|
||||
@@ -74,12 +93,6 @@ predicate dereferencedByOperation(Expr op, Expr e) {
|
||||
e = fc.getArgument(i) and
|
||||
op = fc
|
||||
)
|
||||
or
|
||||
// ptr->Field
|
||||
e = op.(FieldAccess).getQualifier() and isClassPointerType(e.getType())
|
||||
or
|
||||
// ptr->method()
|
||||
e = op.(Call).getQualifier() and isClassPointerType(e.getType())
|
||||
}
|
||||
|
||||
private predicate isClassPointerType(Type t) {
|
||||
|
||||
@@ -240,7 +240,7 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||
*/
|
||||
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
|
||||
exists(IRBlock irb |
|
||||
forex(IRGuardCondition inst | inst = ir | inst.controls(irb, testIsTrue)) and
|
||||
ir.controls(irb, testIsTrue) and
|
||||
irb.getAnInstruction().getAst().(ControlFlowNode).getBasicBlock() = controlled and
|
||||
not isUnreachedBlock(irb)
|
||||
)
|
||||
|
||||
@@ -79,13 +79,3 @@ class ArgumentPosition extends int {
|
||||
/** 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -208,6 +208,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. */
|
||||
Type getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
@@ -295,12 +297,3 @@ class ContentApprox = Unit;
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
/**
|
||||
* Gets an additional term that is added to the `join` and `branch` computations to reflect
|
||||
* an additional forward or backwards branching factor that is not taken into account
|
||||
* when calculating the (virtual) dispatch cost.
|
||||
*
|
||||
* Argument `arg` is part of a path from a source to a sink, and `p` is the target parameter.
|
||||
*/
|
||||
int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNode p) { none() }
|
||||
|
||||
@@ -1547,3 +1547,21 @@ class BuiltInBitCast extends BuiltInOperation, @builtinbitcast {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInBitCast" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_trivial` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a type is a trivial type.
|
||||
* ```
|
||||
* template<typename _Tp>
|
||||
* struct is_trivial
|
||||
* : public integral_constant<bool, __is_trivial(_Tp)>
|
||||
* {};
|
||||
* ```
|
||||
*/
|
||||
class BuiltInIsTrivial extends BuiltInOperation, @istrivialexpr {
|
||||
override string toString() { result = "__is_trivial" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInIsTrivial" }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -917,25 +917,46 @@ module RangeStage<
|
||||
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initialBoundedUpper(SemExpr e) {
|
||||
exists(D::Delta d |
|
||||
initialBounded(e, _, d, false, _, _, _) and
|
||||
D::toFloat(d) >= 0
|
||||
)
|
||||
}
|
||||
|
||||
private predicate noOverflow0(SemExpr e, boolean upper) {
|
||||
exists(boolean lower | lower = upper.booleanNot() |
|
||||
semExprDoesNotOverflow(lower, e)
|
||||
or
|
||||
upper = [true, false] and
|
||||
not potentiallyOverflowingExpr(lower, e)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initialBoundedLower(SemExpr e) {
|
||||
exists(D::Delta d |
|
||||
initialBounded(e, _, d, true, _, _, _) and
|
||||
D::toFloat(d) <= 0
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate noOverflow(SemExpr e, boolean upper) {
|
||||
noOverflow0(e, upper)
|
||||
or
|
||||
upper = true and initialBoundedUpper(e)
|
||||
or
|
||||
upper = false and initialBoundedLower(e)
|
||||
}
|
||||
|
||||
predicate bounded(
|
||||
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
initialBounded(e, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
(
|
||||
semExprDoesNotOverflow(upper.booleanNot(), e)
|
||||
or
|
||||
not potentiallyOverflowingExpr(upper.booleanNot(), e)
|
||||
or
|
||||
exists(D::Delta otherDelta |
|
||||
initialBounded(e, _, otherDelta, upper.booleanNot(), _, _, _) and
|
||||
(
|
||||
upper = true and D::toFloat(otherDelta) >= 0
|
||||
or
|
||||
upper = false and D::toFloat(otherDelta) <= 0
|
||||
)
|
||||
)
|
||||
)
|
||||
noOverflow(e, upper)
|
||||
}
|
||||
|
||||
predicate potentiallyOverflowingExpr(boolean positively, SemExpr expr) {
|
||||
|
||||
@@ -72,7 +72,7 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
|
||||
// Compute `delta` as the constant difference between `x` and `x + 1`.
|
||||
bounded1(any(Instruction instr | instr.getUnconvertedResultExpression() = size),
|
||||
any(LoadInstruction load | load.getUnconvertedResultExpression() = va), delta) and
|
||||
n.asConvertedExpr() = va.getFullyConverted() and
|
||||
n.asExpr() = va and
|
||||
state = delta
|
||||
)
|
||||
}
|
||||
@@ -210,7 +210,7 @@ private module InterestingPointerAddInstruction {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for the second
|
||||
// projection in the `AllocToInvalidPointerConfig` module.
|
||||
hasSize(source.asConvertedExpr(), _, _)
|
||||
hasSize(source.asExpr(), _, _)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
|
||||
@@ -243,7 +243,7 @@ private module InterestingPointerAddInstruction {
|
||||
*/
|
||||
predicate isInterestingSize(DataFlow::Node n) {
|
||||
exists(DataFlow::Node alloc |
|
||||
hasSize(alloc.asConvertedExpr(), n, _) and
|
||||
hasSize(alloc.asExpr(), n, _) and
|
||||
flow(alloc, _)
|
||||
)
|
||||
}
|
||||
@@ -268,7 +268,7 @@ private module Config implements ProductFlow::StateConfigSig {
|
||||
// we use `state2` to remember that there was an offset (in this case an offset of `1`) added
|
||||
// to the size of the allocation. This state is then checked in `isSinkPair`.
|
||||
exists(unit) and
|
||||
hasSize(allocSource.asConvertedExpr(), sizeSource, sizeAddend)
|
||||
hasSize(allocSource.asExpr(), sizeSource, sizeAddend)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit1() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
|
||||
|
||||
Reference in New Issue
Block a user