Merge tag 'codeql-cli/latest'

Compatible with the latest released version of the CodeQL CLI
This commit is contained in:
Dilan
2023-10-11 13:02:07 +00:00
1541 changed files with 135924 additions and 95792 deletions

View File

@@ -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() {

View File

@@ -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)))
}

View File

@@ -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) {

View File

@@ -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)
)

View File

@@ -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()
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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() }

View File

@@ -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" }
}

View File

@@ -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()
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -18,4 +18,6 @@ module CppDataFlow implements InputSig {
import Public
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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
)
)

View File

@@ -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()
)
}

View File

@@ -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.
*/

View File

@@ -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()
}
}

View File

@@ -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) {

View File

@@ -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()
}
}
/**

View File

@@ -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]

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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) {

View File

@@ -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() }