mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Merge remote-tracking branch 'upstream/master' into ir-copy-unloaded-result
This commit is contained in:
@@ -21,6 +21,7 @@ from Variable v
|
||||
where
|
||||
v.isStatic() and
|
||||
v.hasDefinition() and
|
||||
not v.isConstexpr() and
|
||||
not exists(VariableAccess a | a.getTarget() = v) and
|
||||
not v instanceof MemberVariable and
|
||||
not declarationHasSideEffects(v) and
|
||||
|
||||
@@ -2,36 +2,39 @@
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Checking for overflow of integer addition needs to be done with
|
||||
care, because automatic type promotion can prevent the check
|
||||
from working correctly.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Use an explicit cast to make sure that the result of the addition is
|
||||
not implicitly converted to a larger type.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="BadAdditionOverflowCheckExample1.cpp" />
|
||||
<p>
|
||||
On a typical architecture where <tt>short</tt> is 16 bits
|
||||
and <tt>int</tt> is 32 bits, the operands of the addition are
|
||||
automatically promoted to <tt>int</tt>, so it cannot overflow
|
||||
and the result of the comparison is always false.
|
||||
</p>
|
||||
<p>
|
||||
The code below implements the check correctly, by using an
|
||||
explicit cast to make sure that the result of the addition
|
||||
is <tt>unsigned short</tt>.
|
||||
</p>
|
||||
<sample src="BadAdditionOverflowCheckExample2.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li><a href="http://c-faq.com/expr/preservingrules.html">Preserving Rules</a></li>
|
||||
<li><a href="https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942">Understand integer conversion rules</a></li>
|
||||
</references>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Checking for overflow of integer addition needs to be done with
|
||||
care, because automatic type promotion can prevent the check
|
||||
from working as intended, with the same value (<code>true</code>
|
||||
or <code>false</code>) always being returned.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Use an explicit cast to make sure that the result of the addition is
|
||||
not implicitly converted to a larger type.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="BadAdditionOverflowCheckExample1.cpp" />
|
||||
<p>
|
||||
On a typical architecture where <code>short</code> is 16 bits
|
||||
and <code>int</code> is 32 bits, the operands of the addition are
|
||||
automatically promoted to <code>int</code>, so it cannot overflow
|
||||
and the result of the comparison is always false.
|
||||
</p>
|
||||
<p>
|
||||
The code below implements the check correctly, by using an
|
||||
explicit cast to make sure that the result of the addition
|
||||
is <code>unsigned short</code> (which may overflow, in which case
|
||||
the comparison would evaluate to <code>true</code>).
|
||||
</p>
|
||||
<sample src="BadAdditionOverflowCheckExample2.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
<li><a href="http://c-faq.com/expr/preservingrules.html">Preserving Rules</a></li>
|
||||
<li><a href="https://www.securecoding.cert.org/confluence/plugins/servlet/mobile#content/view/20086942">Understand integer conversion rules</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
bool checkOverflow(unsigned short x, unsigned short y) {
|
||||
return (x + y < x); // BAD: x and y are automatically promoted to int.
|
||||
// BAD: comparison is always false due to type promotion
|
||||
return (x + y < x);
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@ where
|
||||
co.getAChild() = chco and
|
||||
not chco.isParenthesised() and
|
||||
not co.isFromUninstantiatedTemplate(_)
|
||||
select co, "Check the comparison operator precedence."
|
||||
select co, "Comparison as an operand to another comparison."
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.dataflow.RecursionPrevention
|
||||
|
||||
/**
|
||||
* A buffer which includes an allocation size.
|
||||
*/
|
||||
abstract class BufferWithSize extends DataFlow::Node {
|
||||
abstract Expr getSizeExpr();
|
||||
|
||||
BufferAccess getAnAccess() {
|
||||
any(BufferWithSizeConfig bsc).hasFlow(this, DataFlow::exprNode(result.getPointer()))
|
||||
}
|
||||
}
|
||||
|
||||
/** An allocation function. */
|
||||
abstract class Alloc extends Function { }
|
||||
|
||||
/**
|
||||
* Allocation functions identified by the QL for C/C++ standard library.
|
||||
*/
|
||||
class DefaultAlloc extends Alloc {
|
||||
DefaultAlloc() { allocationFunction(this) }
|
||||
}
|
||||
|
||||
/** A buffer created through a call to an allocation function. */
|
||||
class AllocBuffer extends BufferWithSize {
|
||||
FunctionCall call;
|
||||
|
||||
AllocBuffer() {
|
||||
asExpr() = call and
|
||||
call.getTarget() instanceof Alloc
|
||||
}
|
||||
|
||||
override Expr getSizeExpr() { result = call.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Find accesses of buffers for which we have a size expression.
|
||||
*/
|
||||
private class BufferWithSizeConfig extends TaintTracking::Configuration {
|
||||
BufferWithSizeConfig() { this = "BufferWithSize" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) { n = any(BufferWithSize b) }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) { n.asExpr() = any(BufferAccess ae).getPointer() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node s) {
|
||||
s = any(BufferWithSize b) and
|
||||
s.asExpr().getControlFlowScope() instanceof Alloc
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An access (read or write) to a buffer, provided as a pair of
|
||||
* a pointer to the buffer and the length of data to be read or written.
|
||||
* Extend this class to support different kinds of buffer access.
|
||||
*/
|
||||
abstract class BufferAccess extends Locatable {
|
||||
/** Gets the pointer to the buffer being accessed. */
|
||||
abstract Expr getPointer();
|
||||
|
||||
/** Gets the length of the data being read or written by this buffer access. */
|
||||
abstract Expr getAccessedLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* A buffer access through an array expression.
|
||||
*/
|
||||
class ArrayBufferAccess extends BufferAccess, ArrayExpr {
|
||||
override Expr getPointer() { result = this.getArrayBase() }
|
||||
|
||||
override Expr getAccessedLength() { result = this.getArrayOffset() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A buffer access through an overloaded array expression.
|
||||
*/
|
||||
class OverloadedArrayBufferAccess extends BufferAccess, OverloadedArrayExpr {
|
||||
override Expr getPointer() { result = this.getQualifier() }
|
||||
|
||||
override Expr getAccessedLength() { result = this.getAnArgument() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A buffer access through pointer arithmetic.
|
||||
*/
|
||||
class PointerArithmeticAccess extends BufferAccess, Expr {
|
||||
PointerArithmeticOperation p;
|
||||
|
||||
PointerArithmeticAccess() {
|
||||
this = p and
|
||||
p.getAnOperand().getType().getUnspecifiedType() instanceof IntegralType and
|
||||
not p.getParent() instanceof ComparisonOperation
|
||||
}
|
||||
|
||||
override Expr getPointer() {
|
||||
result = p.getAnOperand() and
|
||||
result.getType().getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() {
|
||||
result = p.getAnOperand() and
|
||||
result.getType().getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A pair of buffer accesses through a call to memcpy.
|
||||
*/
|
||||
class MemCpy extends BufferAccess, FunctionCall {
|
||||
MemCpy() { getTarget().hasName("memcpy") }
|
||||
|
||||
override Expr getPointer() {
|
||||
result = getArgument(0) or
|
||||
result = getArgument(1)
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class StrncpySizeExpr extends BufferAccess, FunctionCall {
|
||||
StrncpySizeExpr() { getTarget().hasName("strncpy") }
|
||||
|
||||
override Expr getPointer() {
|
||||
result = getArgument(0) or
|
||||
result = getArgument(1)
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class RecvSizeExpr extends BufferAccess, FunctionCall {
|
||||
RecvSizeExpr() { getTarget().hasName("recv") }
|
||||
|
||||
override Expr getPointer() { result = getArgument(1) }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class SendSizeExpr extends BufferAccess, FunctionCall {
|
||||
SendSizeExpr() { getTarget().hasName("send") }
|
||||
|
||||
override Expr getPointer() { result = getArgument(1) }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class SnprintfSizeExpr extends BufferAccess, FunctionCall {
|
||||
SnprintfSizeExpr() { getTarget().hasName("snprintf") }
|
||||
|
||||
override Expr getPointer() { result = getArgument(0) }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
}
|
||||
|
||||
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
|
||||
MemcmpSizeExpr() { getTarget().hasName("Memcmp") }
|
||||
|
||||
override Expr getPointer() {
|
||||
result = getArgument(0) or
|
||||
result = getArgument(1)
|
||||
}
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(2) }
|
||||
}
|
||||
|
||||
class MallocSizeExpr extends BufferAccess, FunctionCall {
|
||||
MallocSizeExpr() { getTarget().hasName("malloc") }
|
||||
|
||||
override Expr getPointer() { none() }
|
||||
|
||||
override Expr getAccessedLength() { result = getArgument(1) }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
int get_number_from_network();
|
||||
|
||||
int process_network(int[] buff, int buffSize) {
|
||||
int i = ntohl(get_number_from_network());
|
||||
return buff[i];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
uint32_t get_number_from_network();
|
||||
|
||||
int process_network(int[] buff, uint32_t buffSize) {
|
||||
uint32_t i = ntohl(get_number_from_network());
|
||||
if (i < buffSize) {
|
||||
return buff[i];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import BufferAccess
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
class NetworkFunctionCall extends FunctionCall {
|
||||
NetworkFunctionCall() {
|
||||
getTarget().hasName("ntohd") or
|
||||
getTarget().hasName("ntohf") or
|
||||
getTarget().hasName("ntohl") or
|
||||
getTarget().hasName("ntohll") or
|
||||
getTarget().hasName("ntohs")
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
|
||||
NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
node.asExpr() = any(BufferAccess ba).getAccessedLength()
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
exists(GuardCondition gc, GVN gvn |
|
||||
gc.getAChild*() = gvn.getAnExpr() and
|
||||
globalValueNumber(node.asExpr()) = gvn and
|
||||
gc.controls(node.asExpr().getBasicBlock(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Data received over a network connection may be received in a different byte order than
|
||||
the byte order used by the local host, making the data difficult to process. To address this,
|
||||
data received over the wire is usually converted to host byte order by a call to a network-to-host
|
||||
byte order function, such as <code>ntohl</code>.
|
||||
</p>
|
||||
<p>
|
||||
The use of a network-to-host byte order function is therefore a good indicator that the returned
|
||||
value is unvalidated data retrieved from the network, and should not be used without further
|
||||
validation. In particular, the returned value should not be used as an array index or array length
|
||||
value without validation, as this could result in a buffer overflow vulnerability.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Validate data returned by network-to-host byte order functions before use and especially before
|
||||
using the value as an array index or bound.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the example below, network data is retrieved and passed to <code>ntohl</code> to convert
|
||||
it to host byte order. The data is then used as an index in an array access expression. However,
|
||||
there is no validation that the data returned by <code>ntohl</code> is within the bounds of the array,
|
||||
which could lead to reading outside the bounds of the buffer.
|
||||
</p>
|
||||
<sample src="NtohlArrayBad.cpp" />
|
||||
<p>In the corrected example, the returned data is validated against the known size of the buffer,
|
||||
before being used as an array index.</p>
|
||||
<sample src="NtohlArrayGood.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
<a href="https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-ntohl">
|
||||
ntohl - winsock reference
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://linux.die.net/man/3/ntohl">
|
||||
ntohl - Linux man page
|
||||
</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @id cpp/network-to-host-function-as-array-bound
|
||||
* @name Untrusted network-to-host usage
|
||||
* @description Using the result of a network-to-host byte order function, such as ntohl, as an
|
||||
* array bound or length value without checking it may result in buffer overflows or
|
||||
* other vulnerabilties.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import NtohlArrayNoBound
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
from NetworkToBufferSizeConfiguration bufConfig, DataFlow::Node source, DataFlow::Node sink
|
||||
where bufConfig.hasFlow(source, sink)
|
||||
select sink, "Unchecked use of data from network function $@", source, source.toString()
|
||||
@@ -2,29 +2,45 @@ import cpp
|
||||
|
||||
class SALMacro extends Macro {
|
||||
SALMacro() {
|
||||
this.getFile().getBaseName() = "sal.h" or
|
||||
this.getFile().getBaseName() = "specstrings_strict.h" or
|
||||
this.getFile().getBaseName() = "specstrings.h"
|
||||
exists(string filename | filename = this.getFile().getBaseName() |
|
||||
filename = "sal.h" or
|
||||
filename = "specstrings_strict.h" or
|
||||
filename = "specstrings.h" or
|
||||
filename = "w32p.h" or
|
||||
filename = "minwindef.h"
|
||||
) and
|
||||
(
|
||||
// Dialect for Windows 8 and above
|
||||
this.getName().matches("\\_%\\_")
|
||||
or
|
||||
// Dialect for Windows 7
|
||||
this.getName().matches("\\_\\_%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
|
||||
|
||||
class SALAnnotation extends MacroInvocation {
|
||||
SALAnnotation() {
|
||||
this.getMacro() instanceof SALMacro and
|
||||
not exists(this.getParentInvocation())
|
||||
isTopLevelMacroAccess(this)
|
||||
}
|
||||
|
||||
/** Returns the `Declaration` annotated by `this`. */
|
||||
Declaration getDeclaration() { annotatesAt(this, result.getADeclarationEntry(), _, _) }
|
||||
Declaration getDeclaration() {
|
||||
annotatesAt(this, result.getADeclarationEntry(), _, _) and
|
||||
not result instanceof Type // exclude typedefs
|
||||
}
|
||||
|
||||
/** Returns the `DeclarationEntry` annotated by `this`. */
|
||||
DeclarationEntry getDeclarationEntry() { annotatesAt(this, result, _, _) }
|
||||
DeclarationEntry getDeclarationEntry() {
|
||||
annotatesAt(this, result, _, _) and
|
||||
not result instanceof TypeDeclarationEntry // exclude typedefs
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Particular SAL annotations of interest
|
||||
*/
|
||||
|
||||
class SALCheckReturn extends SALAnnotation {
|
||||
SALCheckReturn() {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
@@ -39,8 +55,8 @@ class SALNotNull extends SALAnnotation {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
not m.getName().matches("%\\_opt\\_%") and
|
||||
(
|
||||
m.getName().matches("\\_In%") or
|
||||
m.getName().matches("\\_Out%") or
|
||||
m.getName().matches("_In%") or
|
||||
m.getName().matches("_Out%") or
|
||||
m.getName() = "_Ret_notnull_"
|
||||
)
|
||||
) and
|
||||
@@ -63,42 +79,124 @@ class SALMaybeNull extends SALAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation details
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation details
|
||||
/**
|
||||
* Holds if `a` annotates the declaration entry `d` and
|
||||
* its start position is the `idx`th position in `file` that holds a SAL element.
|
||||
*/
|
||||
|
||||
private predicate annotatesAt(SALAnnotation a, DeclarationEntry e, File file, int idx) {
|
||||
a = salElementAt(file, idx) and
|
||||
(
|
||||
// Base case: `a` right before `e`
|
||||
e = salElementAt(file, idx + 1)
|
||||
or
|
||||
// Recursive case: `a` right before some annotation on `e`
|
||||
annotatesAt(_, e, file, idx + 1)
|
||||
)
|
||||
}
|
||||
|
||||
library class SALElement extends Element {
|
||||
SALElement() {
|
||||
this instanceof DeclarationEntry or
|
||||
this instanceof SALAnnotation
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `idx`th `SALElement` in `file`. */
|
||||
private SALElement salElementAt(File file, int idx) {
|
||||
interestingLoc(file, result, interestingStartPos(file, idx))
|
||||
predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
|
||||
annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an SALElement element at character `result` comes at
|
||||
* position `idx` in `file`.
|
||||
* Holds if `pos` is the `idx`th position in `file` that holds a SAL element,
|
||||
* which annotates the declaration entry `d` (by occurring before it without
|
||||
* any other declaration entries in between).
|
||||
*/
|
||||
private int interestingStartPos(File file, int idx) {
|
||||
result = rank[idx](int otherStart | interestingLoc(file, _, otherStart))
|
||||
// For performance reasons, do not mention the annotation itself here,
|
||||
// but compute with positions instead. This performs better on databases
|
||||
// with many annotations at the same position.
|
||||
private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File file, int idx) {
|
||||
pos = salRelevantPositionAt(file, idx) and
|
||||
salAnnotationPos(pos) and
|
||||
(
|
||||
// Base case: `pos` right before `d`
|
||||
d.(SALElement).getStartPosition() = salRelevantPositionAt(file, idx + 1)
|
||||
or
|
||||
// Recursive case: `pos` right before some annotation on `d`
|
||||
annotatesAtPosition(_, d, file, idx + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `element` in `file` is at character `startPos`. */
|
||||
private predicate interestingLoc(File file, SALElement element, int startPos) {
|
||||
element.getLocation().charLoc(file, startPos, _)
|
||||
/**
|
||||
* A parameter annotated by one or more SAL annotations.
|
||||
*/
|
||||
class SALParameter extends Parameter {
|
||||
/** One of this parameter's annotations. */
|
||||
SALAnnotation a;
|
||||
|
||||
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
|
||||
|
||||
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
|
||||
|
||||
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
|
||||
|
||||
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A SAL element, i.e. a SAL annotation or a declaration entry
|
||||
* that may have SAL annotations.
|
||||
*/
|
||||
library class SALElement extends Element {
|
||||
SALElement() {
|
||||
containsSALAnnotation(this.(DeclarationEntry).getFile()) or
|
||||
this instanceof SALAnnotation
|
||||
}
|
||||
|
||||
predicate hasStartPosition(File file, int line, int col) {
|
||||
exists(Location loc | loc = this.getLocation() |
|
||||
file = loc.getFile() and
|
||||
line = loc.getStartLine() and
|
||||
col = loc.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasEndPosition(File file, int line, int col) {
|
||||
exists(Location loc |
|
||||
loc = this.(FunctionDeclarationEntry).getBlock().getLocation()
|
||||
or
|
||||
this = any(VariableDeclarationEntry vde |
|
||||
vde.isDefinition() and
|
||||
loc = vde.getVariable().getInitializer().getLocation()
|
||||
)
|
||||
|
|
||||
file = loc.getFile() and
|
||||
line = loc.getEndLine() and
|
||||
col = loc.getEndColumn()
|
||||
)
|
||||
}
|
||||
|
||||
SALPosition getStartPosition() {
|
||||
exists(File file, int line, int col |
|
||||
this.hasStartPosition(file, line, col) and
|
||||
result = MkSALPosition(file, line, col)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `file` contains a SAL annotation. */
|
||||
pragma[noinline]
|
||||
private predicate containsSALAnnotation(File file) { any(SALAnnotation a).getFile() = file }
|
||||
|
||||
/**
|
||||
* A source-file position of a `SALElement`. Unlike location, this denotes a
|
||||
* point in the file rather than a range.
|
||||
*/
|
||||
private newtype SALPosition =
|
||||
MkSALPosition(File file, int line, int col) {
|
||||
exists(SALElement e |
|
||||
e.hasStartPosition(file, line, col)
|
||||
or
|
||||
e.hasEndPosition(file, line, col)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `pos` is the start position of a SAL annotation. */
|
||||
pragma[noinline]
|
||||
private predicate salAnnotationPos(SALPosition pos) {
|
||||
any(SALAnnotation a).(SALElement).getStartPosition() = pos
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `idx`th position in `file` that holds a SAL element,
|
||||
* ordering positions lexicographically by their start line and start column.
|
||||
*/
|
||||
private SALPosition salRelevantPositionAt(File file, int idx) {
|
||||
result = rank[idx](SALPosition pos, int line, int col |
|
||||
pos = MkSALPosition(file, line, col)
|
||||
|
|
||||
pos order by line, col
|
||||
)
|
||||
}
|
||||
|
||||
25
cpp/ql/src/Microsoft/SAL/IgnoreReturnValueSAL.ql
Normal file
25
cpp/ql/src/Microsoft/SAL/IgnoreReturnValueSAL.ql
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @name SAL requires inspecting return value
|
||||
* @description When a return value is discarded even though the SAL annotation
|
||||
* requires inspecting it, a recoverable error may turn into a
|
||||
* whole-program crash.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
* @id cpp/ignorereturnvaluesal
|
||||
*/
|
||||
|
||||
import Microsoft.SAL
|
||||
|
||||
from Function f, FunctionCall call
|
||||
where
|
||||
call.getTarget() = f and
|
||||
call instanceof ExprInVoidContext and
|
||||
any(SALCheckReturn a).getDeclaration() = f and
|
||||
not any(Options o).okToIgnoreReturnValue(call)
|
||||
select call, "Return value of $@ discarded although a SAL annotation " + "requires inspecting it.",
|
||||
f, f.getName()
|
||||
@@ -5,7 +5,7 @@
|
||||
* @id cpp/comparison-with-wider-type
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-190
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>A common pattern is to initialize a local variable by calling another function (an
|
||||
"initialization" function) with the address of the local variable as a pointer argument. That
|
||||
function is then responsible for writing to the memory location referenced by the pointer.</p>
|
||||
|
||||
<p>In some cases, the called function may not always write to the memory pointed to by the
|
||||
pointer argument. In such cases, the function will typically return a "status" code, informing the
|
||||
caller as to whether the initialization succeeded or not. If the caller does not check the status
|
||||
code before reading the local variable, it may read unitialized memory, which can result in
|
||||
unexpected behavior.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>When using a initialization function that does not guarantee to initialize the memory pointed to
|
||||
by the passed pointer, and returns a status code to indicate whether such initialization occurred,
|
||||
the status code should be checked before reading from the local variable.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this hypothetical example we have code for managing a series of devices. The code
|
||||
includes a <code>DeviceConfig</code> struct that can represent properties about each device.
|
||||
The <code>initDeviceConfig</code> function can be called to initialize one of these structures, by
|
||||
providing a "device number", which can be used to look up the appropriate properties in some data
|
||||
store. If an invalid device number is provided, the function returns a status code of
|
||||
<code>-1</code>, and does not initialize the provided pointer.</p>
|
||||
|
||||
<p>In the first code sample below, the <code>notify</code> function calls the
|
||||
<code>initDeviceConfig</code> function with a pointer to the local variable <code>config</code>,
|
||||
which is then subsequently accessed to fetch properties of the device. However, the code does not
|
||||
check the return value from the function call to <code>initDeviceConfig</code>. If the
|
||||
device number passed to the <code>notify</code> function was invalid, the
|
||||
<code>initDeviceConfig</code> function will leave the <code>config</code> variable uninitialized,
|
||||
which will result in the <code>notify</code> function accessing uninitialized memory.</p>
|
||||
|
||||
<sample src="ConditionallyUninitializedVariableBad.c" />
|
||||
|
||||
<p>To fix this, the code needs to check that the return value of the call to
|
||||
<code>initDeviceConfig</code> is zero. If that is true, then the calling code can safely assume
|
||||
that the local variable has been initialized.</p>
|
||||
|
||||
<sample src="ConditionallyUninitializedVariableGood.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
Wikipedia:
|
||||
<a href="https://en.wikipedia.org/wiki/Uninitialized_variable">Uninitialized variable</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Conditionally uninitialized variable
|
||||
* @description When an initialization function is used to initialize a local variable, but the
|
||||
* returned status code is not checked, the variable may be left in an uninitialized
|
||||
* state, and reading the variable may result in undefined behavior.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @opaque-id SM02313
|
||||
* @id cpp/conditionally-uninitialized-variable
|
||||
* @tags security
|
||||
* external/cwe/cwe-457
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
private import UninitializedVariables
|
||||
|
||||
from
|
||||
ConditionallyInitializedVariable v, ConditionalInitializationFunction f,
|
||||
ConditionalInitializationCall call, string defined, Evidence e
|
||||
where
|
||||
exists(v.getARiskyAccess(f, call, e)) and
|
||||
(
|
||||
if e = DefinitionInSnapshot()
|
||||
then defined = ""
|
||||
else
|
||||
if e = SuggestiveSALAnnotation()
|
||||
then defined = "externally defined (SAL) "
|
||||
else defined = "externally defined (CSV) "
|
||||
)
|
||||
select call,
|
||||
"The status of this call to " + defined +
|
||||
"$@ is not checked, potentially leaving $@ uninitialized.", f, f.getName(), v, v.getName()
|
||||
@@ -0,0 +1,25 @@
|
||||
struct DeviceConfig {
|
||||
bool isEnabled;
|
||||
int channel;
|
||||
};
|
||||
|
||||
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
|
||||
if (deviceNumber >= getMaxDevices()) {
|
||||
// No device with that number, return -1 to indicate failure
|
||||
return -1;
|
||||
}
|
||||
// Device with that number, fetch parameters and initialize struct
|
||||
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
|
||||
ref->channel = fetchDeviceChannel(deviceNumber);
|
||||
// Return 0 to indicate success
|
||||
return 0;
|
||||
}
|
||||
|
||||
int notify(int deviceNumber) {
|
||||
DeviceConfig config;
|
||||
initDeviceConfig(&config, deviceNumber);
|
||||
// BAD: Using config without checking the status code that is returned
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
struct DeviceConfig {
|
||||
bool isEnabled;
|
||||
int channel;
|
||||
};
|
||||
|
||||
int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
|
||||
if (deviceNumber >= getMaxDevices()) {
|
||||
// No device with that number, return -1 to indicate failure
|
||||
return -1;
|
||||
}
|
||||
// Device with that number, fetch parameters and initialize struct
|
||||
ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
|
||||
ref->channel = fetchDeviceChannel(deviceNumber);
|
||||
// Return 0 to indicate success
|
||||
return 0;
|
||||
}
|
||||
|
||||
void notify(int deviceNumber) {
|
||||
DeviceConfig config;
|
||||
int statusCode = initDeviceConfig(&config, deviceNumber);
|
||||
if (statusCode == 0) {
|
||||
// GOOD: Status code returned by initialization function is checked, so this is safe
|
||||
if (config->isEnabled) {
|
||||
notifyChannel(config->channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
690
cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
Normal file
690
cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
Normal file
@@ -0,0 +1,690 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying functions that initialize their arguments.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import external.ExternalArtifact
|
||||
private import semmle.code.cpp.dispatch.VirtualDispatch
|
||||
import semmle.code.cpp.NestedFields
|
||||
import Microsoft.SAL
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/** A context under which a function may be called. */
|
||||
private newtype TContext =
|
||||
/** No specific call context. */
|
||||
NoContext() or
|
||||
/**
|
||||
* The call context is that the given other parameter is null.
|
||||
*
|
||||
* This context is created for all parameters that are null checked in the body of the function.
|
||||
*/
|
||||
ParamNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() } or
|
||||
/**
|
||||
* The call context is that the given other parameter is not null.
|
||||
*
|
||||
* This context is created for all parameters that are null checked in the body of the function.
|
||||
*/
|
||||
ParamNotNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() }
|
||||
|
||||
/**
|
||||
* A context under which a function may be called.
|
||||
*
|
||||
* Some functions may conditionally initialize a parameter depending on the value of another
|
||||
* parameter. Consider:
|
||||
* ```
|
||||
* int MyInitFunction(int* paramToBeInitialized, int* paramToCheck) {
|
||||
* if (!paramToCheck) {
|
||||
* // fail!
|
||||
* return -1;
|
||||
* }
|
||||
* paramToBeInitialized = 0;
|
||||
* }
|
||||
* ```
|
||||
* In this case, whether `paramToBeInitialized` is initialized when this function call completes
|
||||
* depends on whether `paramToCheck` is or is not null. A call-context insensitive analysis will
|
||||
* determine that any call to this function may leave the parameter uninitialized, even if the
|
||||
* argument to paramToCheck is guaranteed to be non-null (`&foo`, for example).
|
||||
*
|
||||
* This class models call contexts that can be considered when calculating whether a given parameter
|
||||
* initializes or not. The supported contexts are:
|
||||
* - `ParamNull(otherParam)` - the given `otherParam` is considered to be null. Applies when
|
||||
* exactly one parameter other than this one is null checked.
|
||||
* - `ParamNotNull(otherParam)` - the given `otherParam` is considered to be not null. Applies when
|
||||
* exactly one parameter other than this one is null checked.
|
||||
* - `NoContext()` - applies in all other circumstances.
|
||||
*/
|
||||
class Context extends TContext {
|
||||
string toString() {
|
||||
this = NoContext() and result = "NoContext"
|
||||
or
|
||||
this = ParamNull(any(Parameter p | result = "ParamNull(" + p.getName() + ")"))
|
||||
or
|
||||
this = ParamNotNull(any(Parameter p | result = "ParamNotNull(" + p.getName() + ")"))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A check against a parameter.
|
||||
*/
|
||||
abstract class ParameterCheck extends Expr {
|
||||
/**
|
||||
* Gets a successor of this check that should be ignored for the given context.
|
||||
*/
|
||||
abstract ControlFlowNode getIgnoredSuccessorForContext(Context c);
|
||||
}
|
||||
|
||||
/** A null-check expression on a parameter. */
|
||||
class ParameterNullCheck extends ParameterCheck {
|
||||
Parameter p;
|
||||
ControlFlowNode nullSuccessor;
|
||||
ControlFlowNode notNullSuccessor;
|
||||
|
||||
ParameterNullCheck() {
|
||||
this.isCondition() and
|
||||
p.getFunction() instanceof InitializationFunction and
|
||||
p.getType().getUnspecifiedType() instanceof PointerType and
|
||||
exists(VariableAccess va | va = p.getAnAccess() |
|
||||
nullSuccessor = getATrueSuccessor() and
|
||||
notNullSuccessor = getAFalseSuccessor() and
|
||||
(
|
||||
va = this.(NotExpr).getOperand() or
|
||||
va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = getAssertedFalseCondition(this) or
|
||||
va = any(NEExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
or
|
||||
nullSuccessor = getAFalseSuccessor() and
|
||||
notNullSuccessor = getATrueSuccessor() and
|
||||
(
|
||||
va = this or
|
||||
va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
|
||||
va = any(EQExpr eq |
|
||||
eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
|
||||
).getAnOperand()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** The parameter being null-checked. */
|
||||
Parameter getParameter() { result = p }
|
||||
|
||||
override ControlFlowNode getIgnoredSuccessorForContext(Context c) {
|
||||
c = ParamNull(p) and result = notNullSuccessor
|
||||
or
|
||||
c = ParamNotNull(p) and result = nullSuccessor
|
||||
}
|
||||
|
||||
/** The successor at which the parameter is confirmed to be null. */
|
||||
ControlFlowNode getNullSuccessor() { result = nullSuccessor }
|
||||
|
||||
/** The successor at which the parameter is confirmed to be not-null. */
|
||||
ControlFlowNode getNotNullSuccessor() { result = notNullSuccessor }
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry in a CSV file in cond-init that contains externally defined functions that are
|
||||
* conditional initializers. These files are typically produced by running the
|
||||
* ConditionallyInitializedFunction companion query.
|
||||
*/
|
||||
class ValidatedExternalCondInitFunction extends ExternalData {
|
||||
ValidatedExternalCondInitFunction() { this.getDataPath().matches("%cond-init%.csv") }
|
||||
|
||||
predicate isExternallyVerified(Function f, int param) {
|
||||
functionSignature(f, getField(1), getField(2)) and param = getFieldAsInt(3)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of evidence used to determine whether a function initializes a parameter.
|
||||
*/
|
||||
newtype Evidence =
|
||||
/**
|
||||
* The function is defined in the snapshot, and the CFG has been analyzed to determine that the
|
||||
* parameter is not initialized on at least one path to the exit.
|
||||
*/
|
||||
DefinitionInSnapshot() or
|
||||
/**
|
||||
* The function is externally defined, but the parameter has an `_out` SAL annotation which
|
||||
* suggests that it is initialized in the function.
|
||||
*/
|
||||
SuggestiveSALAnnotation() or
|
||||
/**
|
||||
* We have been given a CSV file which indicates this parameter is conditionally initialized.
|
||||
*/
|
||||
ExternalEvidence()
|
||||
|
||||
/**
|
||||
* A call to an function which initializes one or more of its parameters.
|
||||
*/
|
||||
class InitializationFunctionCall extends FunctionCall {
|
||||
Expr initializedArgument;
|
||||
|
||||
InitializationFunctionCall() { initializedArgument = getAnInitializedArgument(this) }
|
||||
|
||||
/** Gets a parameter that is initialized by this call. */
|
||||
Parameter getAnInitParameter() { result.getAnAccess() = initializedArgument }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable access which is dereferenced then assigned to.
|
||||
*/
|
||||
private predicate isPointerDereferenceAssignmentTarget(VariableAccess target) {
|
||||
target.getParent().(PointerDereferenceExpr) = any(Assignment e).getLValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* A function which initializes one or more of its parameters.
|
||||
*/
|
||||
class InitializationFunction extends Function {
|
||||
int i;
|
||||
Evidence evidence;
|
||||
|
||||
InitializationFunction() {
|
||||
evidence = DefinitionInSnapshot() and
|
||||
(
|
||||
// Assignment by pointer dereferencing the parameter
|
||||
isPointerDereferenceAssignmentTarget(this.getParameter(i).getAnAccess()) or
|
||||
// Field wise assignment to the parameter
|
||||
any(Assignment e).getLValue() = getAFieldAccess(this.getParameter(i)) or
|
||||
i = this
|
||||
.(MemberFunction)
|
||||
.getAnOverridingFunction+()
|
||||
.(InitializationFunction)
|
||||
.initializedParameter() or
|
||||
getParameter(i) = any(InitializationFunctionCall c).getAnInitParameter()
|
||||
)
|
||||
or
|
||||
// If we have no definition, we look at SAL annotations
|
||||
not this.isDefined() and
|
||||
this.getParameter(i).(SALParameter).isOut() and
|
||||
evidence = SuggestiveSALAnnotation()
|
||||
or
|
||||
// We have some external information that this function conditionally initializes
|
||||
not this.isDefined() and
|
||||
any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and
|
||||
evidence = ExternalEvidence()
|
||||
}
|
||||
|
||||
/** Gets a parameter index which is initialized by this function. */
|
||||
int initializedParameter() { result = i }
|
||||
|
||||
/** Gets a `ControlFlowNode` which assigns a new value to the parameter with the given index. */
|
||||
ControlFlowNode paramReassignment(int index) {
|
||||
index = i and
|
||||
(
|
||||
result = this.getParameter(i).getAnAccess() and
|
||||
(
|
||||
result = any(Assignment a).getLValue().(PointerDereferenceExpr).getOperand()
|
||||
or
|
||||
// Field wise assignment to the parameter
|
||||
result = any(Assignment a).getLValue().(FieldAccess).getQualifier()
|
||||
or
|
||||
// Assignment to a nested field of the parameter
|
||||
result = any(Assignment a).getLValue().(NestedFieldAccess).getUltimateQualifier()
|
||||
or
|
||||
result = getAnInitializedArgument(any(Call c))
|
||||
or
|
||||
exists(IfStmt check | result = check.getCondition().getAChild*() |
|
||||
paramReassignmentCondition(check)
|
||||
)
|
||||
)
|
||||
or
|
||||
result = any(AssumeExpr e |
|
||||
e.getEnclosingFunction() = this and e.getAChild().(Literal).getValue() = "0"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper predicate: holds if the `if` statement `check` contains a
|
||||
* reassignment to the `i`th parameter within its `then` statement.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate paramReassignmentCondition(IfStmt check) {
|
||||
this.paramReassignment(i).getEnclosingStmt().getParentStmt*() = check.getThen()
|
||||
}
|
||||
|
||||
/** Holds if `n` can be reached without the parameter at `index` being reassigned. */
|
||||
predicate paramNotReassignedAt(ControlFlowNode n, int index, Context c) {
|
||||
c = getAContext(index) and
|
||||
(
|
||||
not exists(this.getEntryPoint()) and index = i and n = this
|
||||
or
|
||||
n = this.getEntryPoint() and index = i
|
||||
or
|
||||
exists(ControlFlowNode mid | paramNotReassignedAt(mid, index, c) |
|
||||
n = mid.getASuccessor() and
|
||||
not n = paramReassignment(index) and
|
||||
/*
|
||||
* Ignore successor edges where the parameter is null, because it is then confirmed to be
|
||||
* initialized.
|
||||
*/
|
||||
|
||||
not exists(ParameterNullCheck nullCheck |
|
||||
nullCheck = mid and
|
||||
nullCheck = getANullCheck(index) and
|
||||
n = nullCheck.getNullSuccessor()
|
||||
) and
|
||||
/*
|
||||
* Ignore successor edges which are excluded by the given context
|
||||
*/
|
||||
|
||||
not exists(ParameterCheck paramCheck | paramCheck = mid |
|
||||
n = paramCheck.getIgnoredSuccessorForContext(c)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a null-check on the parameter at `index`. */
|
||||
private ParameterNullCheck getANullCheck(int index) {
|
||||
getParameter(index) = result.getParameter()
|
||||
}
|
||||
|
||||
/** Gets a parameter which is not at the given index. */
|
||||
private Parameter getOtherParameter(int index) {
|
||||
index = i and
|
||||
result = getAParameter() and
|
||||
not result.getIndex() = index
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call `Context` that is applicable when considering whether parameter at the `index` can
|
||||
* be conditionally initialized.
|
||||
*/
|
||||
Context getAContext(int index) {
|
||||
index = i and
|
||||
/*
|
||||
* If there is one and only one other parameter which is null checked in the body of the method,
|
||||
* then we have two contexts to consider - that the other param is null, or that the other param
|
||||
* is not null.
|
||||
*/
|
||||
|
||||
if
|
||||
strictcount(Parameter p |
|
||||
exists(Context c | c = ParamNull(p) or c = ParamNotNull(p)) and
|
||||
p = getOtherParameter(index)
|
||||
) = 1
|
||||
then
|
||||
exists(Parameter p | p = getOtherParameter(index) |
|
||||
result = ParamNull(p) or result = ParamNotNull(p)
|
||||
)
|
||||
else
|
||||
// Otherwise, only consider NoContext.
|
||||
result = NoContext()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function should be whitelisted - that is, not considered as conditionally
|
||||
* initializing its parameters.
|
||||
*/
|
||||
predicate whitelisted() {
|
||||
exists(string name | this.hasName(name) |
|
||||
// Return value is not a success code but the output functions never fail.
|
||||
name.matches("_Interlocked%")
|
||||
or
|
||||
// Functions that never fail, according to MSDN.
|
||||
name = "QueryPerformanceCounter"
|
||||
or
|
||||
name = "QueryPerformanceFrequency"
|
||||
or
|
||||
// Functions that never fail post-Vista, according to MSDN.
|
||||
name = "InitializeCriticalSectionAndSpinCount"
|
||||
or
|
||||
// `rand_s` writes 0 to a non-null argument if it fails, according to MSDN.
|
||||
name = "rand_s"
|
||||
or
|
||||
// IntersectRect initializes the argument regardless of whether the input intersects
|
||||
name = "IntersectRect"
|
||||
or
|
||||
name = "SetRect"
|
||||
or
|
||||
name = "UnionRect"
|
||||
or
|
||||
// These functions appears to have an incorrect CFG, which leads to false positives
|
||||
name = "PhysicalToLogicalDPIPoint"
|
||||
or
|
||||
name = "LogicalToPhysicalDPIPoint"
|
||||
or
|
||||
// Sets NtProductType to default on error
|
||||
name = "RtlGetNtProductType"
|
||||
or
|
||||
// Our CFG is not sophisticated enough to detect that the argument is always initialized
|
||||
name = "StringCchLengthA"
|
||||
or
|
||||
// All paths init the argument, and always returns SUCCESS.
|
||||
name = "RtlUnicodeToMultiByteSize"
|
||||
or
|
||||
// All paths init the argument, and always returns SUCCESS.
|
||||
name = "RtlMultiByteToUnicodeSize"
|
||||
or
|
||||
// All paths init the argument, and always returns SUCCESS.
|
||||
name = "RtlUnicodeToMultiByteN"
|
||||
or
|
||||
// Always initializes argument
|
||||
name = "RtlGetFirstRange"
|
||||
or
|
||||
// Destination range is zeroed out on failure, assuming first two parameters are valid
|
||||
name = "memcpy_s"
|
||||
or
|
||||
// This zeroes the memory unconditionally
|
||||
name = "SeCreateAccessState"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function which initializes one or more of its parameters, but not on all paths.
|
||||
*/
|
||||
class ConditionalInitializationFunction extends InitializationFunction {
|
||||
Context c;
|
||||
|
||||
ConditionalInitializationFunction() {
|
||||
c = this.getAContext(i) and
|
||||
not this.whitelisted() and
|
||||
exists(Type status | status = this.getType().getUnspecifiedType() |
|
||||
status instanceof IntegralType or
|
||||
status instanceof Enum
|
||||
) and
|
||||
not this.getType().getName().toLowerCase() = "size_t" and
|
||||
(
|
||||
/*
|
||||
* If there is no definition, consider this to be conditionally initializing (based on either
|
||||
* SAL or external data).
|
||||
*/
|
||||
|
||||
not evidence = DefinitionInSnapshot()
|
||||
or
|
||||
/*
|
||||
* If this function is defined in this snapshot, then it conditionally initializes if there
|
||||
* is at least one path through the function which doesn't initialize the parameter.
|
||||
*
|
||||
* Explicitly ignore pure virtual functions.
|
||||
*/
|
||||
|
||||
this.isDefined() and
|
||||
this.paramNotReassignedAt(this, i, c) and
|
||||
not this instanceof PureVirtualFunction
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the evidence associated with the given parameter. */
|
||||
Evidence getEvidence(int param) {
|
||||
/*
|
||||
* Note: due to the way the predicate dispatch interacts with fields, this needs to be
|
||||
* implemented on this class, not `InitializationFunction`. If implemented on the latter it
|
||||
* can return evidence that does not result in conditional initialization.
|
||||
*/
|
||||
|
||||
param = i and evidence = result
|
||||
}
|
||||
|
||||
/** Gets the index of a parameter which is conditionally initialized. */
|
||||
int conditionallyInitializedParameter(Context context) { result = i and context = c }
|
||||
}
|
||||
|
||||
/**
|
||||
* More elaborate tracking, flagging cases where the status is checked after
|
||||
* the potentially uninitialized variable has been used, and ignoring cases
|
||||
* where the status is not checked but there is no use of the potentially
|
||||
* uninitialized variable, may be obtained via `getARiskyAccess`.
|
||||
*/
|
||||
class ConditionalInitializationCall extends FunctionCall {
|
||||
ConditionalInitializationFunction target;
|
||||
|
||||
ConditionalInitializationCall() { target = getTarget(this) }
|
||||
|
||||
/** Gets the argument passed for the given parameter to this call. */
|
||||
Expr getArgumentForParameter(Parameter p) {
|
||||
p = getTarget().getAParameter() and
|
||||
result = getArgument(p.getIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an argument conditionally initialized by this call.
|
||||
*/
|
||||
Expr getAConditionallyInitializedArgument(ConditionalInitializationFunction condTarget, Evidence e) {
|
||||
condTarget = target and
|
||||
exists(Context context |
|
||||
result = getAConditionallyInitializedArgument(this, condTarget, context, e)
|
||||
|
|
||||
context = NoContext()
|
||||
or
|
||||
exists(Parameter otherP, Expr otherArg |
|
||||
context = ParamNotNull(otherP) or
|
||||
context = ParamNull(otherP)
|
||||
|
|
||||
otherArg = getArgumentForParameter(otherP) and
|
||||
(otherArg instanceof AddressOfExpr implies context = ParamNotNull(otherP)) and
|
||||
(otherArg.getType() instanceof ArrayType implies context = ParamNotNull(otherP)) and
|
||||
(otherArg.getValue() = "0" implies context = ParamNull(otherP))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess getAConditionallyInitializedVariable() {
|
||||
not result.getTarget().getAnAssignedValue().getASuccessor+() = result and
|
||||
// Should not be assigned field-wise prior to the call.
|
||||
not exists(Assignment a, FieldAccess fa |
|
||||
fa.getQualifier() = result.getTarget().getAnAccess() and
|
||||
a.getLValue() = fa and
|
||||
fa.getASuccessor+() = result
|
||||
) and
|
||||
result = this
|
||||
.getArgument(getTarget(this)
|
||||
.(ConditionalInitializationFunction)
|
||||
.conditionallyInitializedParameter(_))
|
||||
.(AddressOfExpr)
|
||||
.getOperand()
|
||||
}
|
||||
|
||||
Variable getStatusVariable() {
|
||||
exists(AssignExpr a | a.getLValue() = result.getAnAccess() | a.getRValue() = this)
|
||||
or
|
||||
result.getInitializer().getExpr() = this
|
||||
}
|
||||
|
||||
Expr getSuccessCheck() {
|
||||
exists(this.getAFalseSuccessor()) and result = this
|
||||
or
|
||||
result = this.getParent() and
|
||||
(
|
||||
result instanceof NotExpr or
|
||||
result.(EQExpr).getAnOperand().getValue() = "0" or
|
||||
result.(GEExpr).getLesserOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
Expr getFailureCheck() {
|
||||
result = this.getParent() and
|
||||
(
|
||||
result instanceof NotExpr or
|
||||
result.(NEExpr).getAnOperand().getValue() = "0" or
|
||||
result.(LTExpr).getLesserOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate inCheckedContext() {
|
||||
exists(Call parent | this = parent.getAnArgument() |
|
||||
parent.getTarget() instanceof Operator or
|
||||
parent.getTarget().hasName("VerifyOkCatastrophic")
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowNode uncheckedReaches(LocalVariable var) {
|
||||
(
|
||||
not exists(var.getInitializer()) and
|
||||
var = this.getAConditionallyInitializedVariable().getTarget() and
|
||||
if exists(this.getFailureCheck())
|
||||
then result = this.getFailureCheck().getATrueSuccessor()
|
||||
else
|
||||
if exists(this.getSuccessCheck())
|
||||
then result = this.getSuccessCheck().getAFalseSuccessor()
|
||||
else (
|
||||
result = this.getASuccessor() and not this.inCheckedContext()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ControlFlowNode mid | mid = uncheckedReaches(var) |
|
||||
not mid = getStatusVariable().getAnAccess() and
|
||||
not mid = var.getAnAccess() and
|
||||
not exists(VariableAccess write | result = write and write = var.getAnAccess() |
|
||||
write = any(AssignExpr a).getLValue() or
|
||||
write = any(AddressOfExpr a).getOperand()
|
||||
) and
|
||||
result = mid.getASuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
VariableAccess getARiskyRead(Function f) {
|
||||
f = this.getTarget() and
|
||||
exists(this.getFile().getRelativePath()) and
|
||||
result = this.uncheckedReaches(result.getTarget()) and
|
||||
not this.(GuardCondition).controls(result.getBasicBlock(), _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of an argument to the call which is initialized by the call.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
int initializedArgument(Call call) {
|
||||
exists(InitializationFunction target |
|
||||
target = getTarget(call) and
|
||||
result = target.initializedParameter()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an argument which is initialized by the call.
|
||||
*/
|
||||
Expr getAnInitializedArgument(Call call) { result = call.getArgument(initializedArgument(call)) }
|
||||
|
||||
/**
|
||||
* Gets the position of an argument to the call to the target which is conditionally initialized by
|
||||
* the call, under the given context and evidence.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
int conditionallyInitializedArgument(
|
||||
Call call, ConditionalInitializationFunction target, Context c, Evidence e
|
||||
) {
|
||||
target = getTarget(call) and
|
||||
c = target.getAContext(result) and
|
||||
e = target.getEvidence(result) and
|
||||
result = target.conditionallyInitializedParameter(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an argument which is conditionally initialized by the call to the given target under the given context and evidence.
|
||||
*/
|
||||
Expr getAConditionallyInitializedArgument(
|
||||
Call call, ConditionalInitializationFunction target, Context c, Evidence e
|
||||
) {
|
||||
result = call.getArgument(conditionallyInitializedArgument(call, target, c, e))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type signature for the functions parameters.
|
||||
*/
|
||||
string typeSig(Function f) {
|
||||
result = concat(int i, Type pt |
|
||||
pt = f.getParameter(i).getType()
|
||||
|
|
||||
pt.getUnspecifiedType().toString(), "," order by i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds where qualifiedName and typeSig make up the signature for the function.
|
||||
*/
|
||||
predicate functionSignature(Function f, string qualifiedName, string typeSig) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
typeSig = typeSig(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible definition for the undefined function by matching the undefined function name
|
||||
* and parameter arity with a defined function.
|
||||
*
|
||||
* This is useful for identifying call to target dependencies across libraries, where the libraries
|
||||
* are never statically linked together.
|
||||
*/
|
||||
Function getAPossibleDefinition(Function undefinedFunction) {
|
||||
not undefinedFunction.isDefined() and
|
||||
exists(string qn, string typeSig |
|
||||
functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
|
||||
) and
|
||||
result.isDefined()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible target for the Call, using the name and parameter matching if we did not associate
|
||||
* this call with a specific definition at link or compile time, and performing simple virtual
|
||||
* dispatch resolution.
|
||||
*/
|
||||
Function getTarget(Call c) {
|
||||
if VirtualDispatch::getAViableTarget(c).isDefined()
|
||||
then
|
||||
/*
|
||||
* If there is at least one defined target after performing some simple virtual dispatch
|
||||
* resolution, then the result is all the defined targets.
|
||||
*/
|
||||
|
||||
result = VirtualDispatch::getAViableTarget(c) and
|
||||
result.isDefined()
|
||||
else
|
||||
if exists(getAPossibleDefinition(VirtualDispatch::getAViableTarget(c)))
|
||||
then
|
||||
/*
|
||||
* If we can use the heuristic matching of functions to find definitions for some of the viable
|
||||
* targets, return those.
|
||||
*/
|
||||
|
||||
result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
|
||||
else
|
||||
// Otherwise, the result is the undefined `Function` instances
|
||||
result = VirtualDispatch::getAViableTarget(c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an access of a field on `Variable` v.
|
||||
*/
|
||||
FieldAccess getAFieldAccess(Variable v) {
|
||||
exists(VariableAccess va, Expr qualifierExpr |
|
||||
// Find an access of the variable, or an AddressOfExpr that has the access
|
||||
va = v.getAnAccess() and
|
||||
(
|
||||
qualifierExpr = va or
|
||||
qualifierExpr.(AddressOfExpr).getOperand() = va
|
||||
)
|
||||
|
|
||||
// Direct field access
|
||||
qualifierExpr = result.getQualifier()
|
||||
or
|
||||
// Nested field access
|
||||
qualifierExpr = result.(NestedFieldAccess).getUltimateQualifier()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition which is asserted to be false by the given `ne` expression, according to this pattern:
|
||||
* ```
|
||||
* int a = !!result;
|
||||
* if (!a) { // <- ne
|
||||
* ....
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
Expr getAssertedFalseCondition(NotExpr ne) {
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and
|
||||
ne.getOperand() = v.getAnAccess() and
|
||||
nonAssignedVariable(v)
|
||||
// and not passed by val?
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate nonAssignedVariable(Variable v) { not exists(v.getAnAssignment()) }
|
||||
190
cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll
Normal file
190
cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* A module for identifying conditionally initialized variables.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import InitializationFunctions
|
||||
|
||||
// Optimised reachability predicates
|
||||
private predicate reaches(ControlFlowNode a, ControlFlowNode b) = fastTC(successor/2)(a, b)
|
||||
|
||||
private predicate successor(ControlFlowNode a, ControlFlowNode b) { b = a.getASuccessor() }
|
||||
|
||||
class WhitelistedCallsConfig extends string {
|
||||
WhitelistedCallsConfig() { this = "config" }
|
||||
|
||||
abstract predicate isWhitelisted(Call c);
|
||||
}
|
||||
|
||||
abstract class WhitelistedCall extends Call {
|
||||
override Function getTarget() { none() }
|
||||
}
|
||||
|
||||
private predicate hasConditionalInitialization(
|
||||
ConditionalInitializationFunction f, ConditionalInitializationCall call, LocalVariable v,
|
||||
VariableAccess initAccess, Evidence e
|
||||
) {
|
||||
// Ignore whitelisted calls
|
||||
not call instanceof WhitelistedCall and
|
||||
f = getTarget(call) and
|
||||
initAccess = v.getAnAccess() and
|
||||
initAccess = call.getAConditionallyInitializedArgument(f, e).(AddressOfExpr).getOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that can be conditionally initialized by a call.
|
||||
*/
|
||||
class ConditionallyInitializedVariable extends LocalVariable {
|
||||
ConditionalInitializationCall call;
|
||||
ConditionalInitializationFunction f;
|
||||
VariableAccess initAccess;
|
||||
Evidence e;
|
||||
|
||||
ConditionallyInitializedVariable() {
|
||||
// Find a call that conditionally initializes this variable
|
||||
hasConditionalInitialization(f, call, this, initAccess, e) and
|
||||
// Ignore cases where the variable is assigned prior to the call
|
||||
not reaches(getAnAssignedValue(), initAccess) and
|
||||
// Ignore cases where the variable is assigned field-wise prior to the call.
|
||||
not exists(FieldAccess fa |
|
||||
exists(Assignment a |
|
||||
fa = getAFieldAccess(this) and
|
||||
a.getLValue() = fa
|
||||
)
|
||||
|
|
||||
reaches(fa, initAccess)
|
||||
) and
|
||||
// Ignore cases where the variable is assigned by a prior call to an initialization function
|
||||
not exists(Call c |
|
||||
getAnAccess() = getAnInitializedArgument(c).(AddressOfExpr).getOperand() and
|
||||
reaches(c, initAccess)
|
||||
) and
|
||||
/*
|
||||
* Static local variables with constant initializers do not have the initializer expr as part of
|
||||
* the CFG, but should always be considered as initialized, so exclude them.
|
||||
*/
|
||||
|
||||
not exists(getInitializer().getExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access of the variable `v` which is not used as an lvalue, and not used as an argument
|
||||
* to an initialization function.
|
||||
*/
|
||||
private VariableAccess getAReadAccess() {
|
||||
result = this.getAnAccess() and
|
||||
// Not used as an lvalue
|
||||
not result = any(AssignExpr a).getLValue() and
|
||||
// Not passed to another initialization function
|
||||
not exists(Call c, int j | j = c.getTarget().(InitializationFunction).initializedParameter() |
|
||||
result = c.getArgument(j).(AddressOfExpr).getOperand()
|
||||
) and
|
||||
// Not a pointless read
|
||||
not result = any(ExprStmt es).getExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read access of variable `v` that occurs after the `initializingCall`.
|
||||
*/
|
||||
private VariableAccess getAReadAccessAfterCall(ConditionalInitializationCall initializingCall) {
|
||||
// Variable associated with this particular call
|
||||
call = initializingCall and
|
||||
// Access is a meaningful read access
|
||||
result = getAReadAccess() and
|
||||
// Which occurs after the call
|
||||
reaches(call, result) and
|
||||
/*
|
||||
* Ignore risky accesses which are arguments to calls which also include another parameter to
|
||||
* the original call. This is an attempt to eliminate results where the "status" can be checked
|
||||
* through another parameter that assigned as part of the original call.
|
||||
*/
|
||||
|
||||
not exists(Call c |
|
||||
c.getAnArgument() = result or
|
||||
c.getAnArgument().(AddressOfExpr).getOperand() = result
|
||||
|
|
||||
exists(LocalVariable lv |
|
||||
call.getAnArgument().(AddressOfExpr).getOperand() = lv.getAnAccess() and
|
||||
not lv = this
|
||||
|
|
||||
c.getAnArgument() = lv.getAnAccess()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the variable that is risky because the variable may not be initialized after
|
||||
* the `call`, and the status of the call is never checked.
|
||||
*/
|
||||
VariableAccess getARiskyAccessWithNoStatusCheck(
|
||||
ConditionalInitializationFunction initializingFunction,
|
||||
ConditionalInitializationCall initializingCall, Evidence evidence
|
||||
) {
|
||||
// Variable associated with this particular call
|
||||
call = initializingCall and
|
||||
initializingFunction = f and
|
||||
e = evidence and
|
||||
result = getAReadAccessAfterCall(initializingCall) and
|
||||
(
|
||||
// Access is risky because status return code ignored completely
|
||||
call instanceof ExprInVoidContext
|
||||
or
|
||||
// Access is risky because status return code ignored completely
|
||||
exists(LocalVariable status | call = status.getAnAssignedValue() |
|
||||
not exists(status.getAnAccess())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the variable that is risky because the variable may not be initialized after
|
||||
* the `call`, and the status of the call is only checked after the risky access.
|
||||
*/
|
||||
VariableAccess getARiskyAccessBeforeStatusCheck(
|
||||
ConditionalInitializationFunction initializingFunction,
|
||||
ConditionalInitializationCall initializingCall, Evidence evidence
|
||||
) {
|
||||
// Variable associated with this particular call
|
||||
call = initializingCall and
|
||||
initializingFunction = f and
|
||||
e = evidence and
|
||||
result = getAReadAccessAfterCall(initializingCall) and
|
||||
exists(LocalVariable status, Assignment a |
|
||||
a.getRValue() = call and
|
||||
call = status.getAnAssignedValue() and
|
||||
// There exists a check of the status code
|
||||
definitionUsePair(status, a, _) and
|
||||
// And the check of the status code does not occur before the risky access
|
||||
not exists(VariableAccess statusAccess |
|
||||
definitionUsePair(status, a, statusAccess) and
|
||||
reaches(statusAccess, result)
|
||||
) and
|
||||
// Ignore cases where the assignment to the status code is used directly
|
||||
a instanceof ExprInVoidContext and
|
||||
/*
|
||||
* Ignore risky accesses which are arguments to calls which also include the status code.
|
||||
* If both the risky value and status code are passed to a different function, that
|
||||
* function is responsible for checking the status code.
|
||||
*/
|
||||
|
||||
not exists(Call c |
|
||||
c.getAnArgument() = result or
|
||||
c.getAnArgument().(AddressOfExpr).getOperand() = result
|
||||
|
|
||||
definitionUsePair(status, a, c.getAnArgument())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the variable that is risky because the variable may not be initialized after
|
||||
* the `call`.
|
||||
*/
|
||||
VariableAccess getARiskyAccess(
|
||||
ConditionalInitializationFunction initializingFunction,
|
||||
ConditionalInitializationCall initializingCall, Evidence evidence
|
||||
) {
|
||||
result = getARiskyAccessBeforeStatusCheck(initializingFunction, initializingCall, evidence) or
|
||||
result = getARiskyAccessWithNoStatusCheck(initializingFunction, initializingCall, evidence)
|
||||
}
|
||||
}
|
||||
41
cpp/ql/src/semmle/code/cpp/NestedFields.qll
Normal file
41
cpp/ql/src/semmle/code/cpp/NestedFields.qll
Normal file
@@ -0,0 +1,41 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Gets a `Field` that is nested within the given `Struct`.
|
||||
*
|
||||
* This identifies `Field`s which are located in the same memory
|
||||
*/
|
||||
private Field getANestedField(Struct s) {
|
||||
result = s.getAField()
|
||||
or
|
||||
exists(NestedStruct ns |
|
||||
s = ns.getDeclaringType() and
|
||||
result = getANestedField(ns)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps a series of field accesses to determine the inner-most qualifier.
|
||||
*/
|
||||
private Expr getUltimateQualifier(FieldAccess fa) {
|
||||
exists(Expr qualifier | qualifier = fa.getQualifier() |
|
||||
result = getUltimateQualifier(qualifier)
|
||||
or
|
||||
not qualifier instanceof FieldAccess and result = qualifier
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Accesses to nested fields.
|
||||
*/
|
||||
class NestedFieldAccess extends FieldAccess {
|
||||
Expr ultimateQualifier;
|
||||
|
||||
NestedFieldAccess() {
|
||||
ultimateQualifier = getUltimateQualifier(this) and
|
||||
getTarget() = getANestedField(ultimateQualifier.getType().stripType())
|
||||
}
|
||||
|
||||
/** Gets the ultimate qualifier of this nested field access. */
|
||||
Expr getUltimateQualifier() { result = ultimateQualifier }
|
||||
}
|
||||
@@ -7,3 +7,15 @@
|
||||
|
||||
import cpp
|
||||
import PrintAST
|
||||
|
||||
/**
|
||||
* Temporarily tweak this class or make a copy to control which functions are
|
||||
* printed.
|
||||
*/
|
||||
class Cfg extends PrintASTConfiguration {
|
||||
/**
|
||||
* TWEAK THIS PREDICATE AS NEEDED.
|
||||
* Holds if the AST for `func` should be printed.
|
||||
*/
|
||||
override predicate shouldPrintFunction(Function func) { any() }
|
||||
}
|
||||
|
||||
@@ -610,7 +610,9 @@ class FloatingPointType extends ArithmeticType {
|
||||
(
|
||||
kind >= 24 and kind <= 32
|
||||
or
|
||||
kind = 38
|
||||
kind >= 38 and kind <= 42
|
||||
or
|
||||
kind >= 45 and kind <= 50
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ private predicate exprInVoidContext(Expr e) {
|
||||
exists(CommaExpr c | c.getLeftOperand() = e)
|
||||
or
|
||||
exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext)
|
||||
or
|
||||
exists(ForStmt for | for.getUpdate() = e)
|
||||
) and
|
||||
not e instanceof Qualifier and
|
||||
not e.getActualType() instanceof VoidType
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
@@ -680,12 +682,16 @@ VariableAccess getAnAccessToAssignedVariable(Expr assign) {
|
||||
*
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends Expr {
|
||||
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `branch`. */
|
||||
abstract deprecated predicate checks(Expr e, boolean branch);
|
||||
class BarrierGuard extends GuardCondition {
|
||||
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract predicate checks(Expr e, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
none() // stub
|
||||
final ExprNode getAGuardedNode() {
|
||||
exists(GVN value, boolean branch |
|
||||
result.getExpr() = value.getAnExpr() and
|
||||
this.checks(value.getAnExpr(), branch) and
|
||||
this.controls(result.getExpr().getBasicBlock(), branch)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
83
cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll
Normal file
83
cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll
Normal file
@@ -0,0 +1,83 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A module for performing simple virtual dispatch analysis.
|
||||
*/
|
||||
module VirtualDispatch {
|
||||
/**
|
||||
* Gets a possible implementation target when the given function is the static target of a virtual call.
|
||||
*/
|
||||
private MemberFunction getAPossibleImplementation(MemberFunction staticTarget) {
|
||||
/*
|
||||
* `IUnknown` is a COM interface with many sub-types, and many overrides (tens of thousands on
|
||||
* some databases), so we ignore any member functions defined within that interface.
|
||||
*/
|
||||
|
||||
not staticTarget.getDeclaringType().hasName("IUnknown") and
|
||||
result = staticTarget.getAnOverridingFunction*()
|
||||
}
|
||||
|
||||
/** Gets the static type of the qualifier expression for the given call. */
|
||||
private Class getCallQualifierType(FunctionCall c) {
|
||||
result = c.getQualifier().getType().stripType() and
|
||||
/*
|
||||
* `IUnknown` is a COM interface with many sub-types (tens of thousands on some databases), so
|
||||
* we ignore any cases where the qualifier type is that interface.
|
||||
*/
|
||||
|
||||
not result.hasName("IUnknown")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable target for the given function call.
|
||||
*
|
||||
* If `c` is a virtual call, then we will perform a simple virtual dispatch analysis to return
|
||||
* the `Function` instances which might be a viable target, based on an analysis of the declared
|
||||
* type of the qualifier expression.
|
||||
*
|
||||
* (This analysis is imprecise: it looks for subtypes of the declared type of the qualifier expression
|
||||
* and the possible implementations of `c.getTarget()` that are declared or inherited by those subtypes.
|
||||
* This does not account for virtual inheritance and the ways this affects dispatch.)
|
||||
*
|
||||
* If `c` is not a virtual call, the result will be `c.getTarget()`.
|
||||
*/
|
||||
Function getAViableTarget(Call c) {
|
||||
exists(Function staticTarget | staticTarget = c.getTarget() |
|
||||
if c.(FunctionCall).isVirtual() and staticTarget instanceof MemberFunction
|
||||
then
|
||||
exists(Class qualifierType, Class qualifierSubType |
|
||||
result = getAPossibleImplementation(staticTarget) and
|
||||
qualifierType = getCallQualifierType(c) and
|
||||
qualifierType = qualifierSubType.getABaseClass*() and
|
||||
mayInherit(qualifierSubType, result) and
|
||||
not cannotInherit(qualifierSubType, result)
|
||||
)
|
||||
else result = staticTarget
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `f` is declared in `c` or a transitive base class of `c`. */
|
||||
private predicate mayInherit(Class c, MemberFunction f) {
|
||||
f.getDeclaringType() = c.getABaseClass*()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` cannot inherit the member function `f`,
|
||||
* i.e. `c` or one of its supertypes overrides `f`.
|
||||
*/
|
||||
private predicate cannotInherit(Class c, MemberFunction f) {
|
||||
exists(Class overridingType, MemberFunction override |
|
||||
cannotInheritHelper(c, f, overridingType, override) and
|
||||
override.overrides+(f)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate cannotInheritHelper(
|
||||
Class c, MemberFunction f, Class overridingType, MemberFunction override
|
||||
) {
|
||||
c.getABaseClass*() = overridingType and
|
||||
override.getDeclaringType() = overridingType and
|
||||
overridingType.getABaseClass+() = f.getDeclaringType()
|
||||
}
|
||||
}
|
||||
@@ -540,6 +540,13 @@ class ErrorExpr extends Expr, @errorexpr {
|
||||
*/
|
||||
class AssumeExpr extends Expr, @assume {
|
||||
override string toString() { result = "__assume(...)" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "AssumeExpr" }
|
||||
|
||||
/**
|
||||
* Gets the operand of the `__assume` expressions.
|
||||
*/
|
||||
Expr getOperand() { this.hasChild(result, 0) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
private predicate accessesVariable(CopyInstruction copy, Variable var) {
|
||||
exists(VariableAddressInstruction va | va.getVariable().getAST() = var |
|
||||
exists(VariableAddressInstruction va | va.getASTVariable() = var |
|
||||
copy.(StoreInstruction).getDestinationAddress() = va
|
||||
or
|
||||
copy.(LoadInstruction).getSourceAddress() = va
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* A newtype wrapper to prevent accidental casts between `Node` and
|
||||
@@ -213,6 +214,14 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
|
||||
*/
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localInstructionFlow(Instruction e1, Instruction e2) {
|
||||
localFlow(instructionNode(e1), instructionNode(e2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
@@ -220,7 +229,7 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
|
||||
|
||||
/**
|
||||
* A guard that validates some expression.
|
||||
* A guard that validates some instruction.
|
||||
*
|
||||
* To use this in a configuration, extend the class and provide a
|
||||
* characteristic predicate precisely specifying the guard, and override
|
||||
@@ -229,11 +238,15 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
|
||||
* It is important that all extending classes in scope are disjoint.
|
||||
*/
|
||||
class BarrierGuard extends IRGuardCondition {
|
||||
/** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `b`. */
|
||||
abstract deprecated predicate checks(Instruction e, boolean b);
|
||||
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
|
||||
abstract predicate checks(Instruction instr, boolean b);
|
||||
|
||||
/** Gets a node guarded by this guard. */
|
||||
final Node getAGuardedNode() {
|
||||
none() // stub
|
||||
exists(ValueNumber value, boolean edge |
|
||||
result.asInstruction() = value.getAnInstruction() and
|
||||
this.checks(value.getAnInstruction(), edge) and
|
||||
this.controls(result.asInstruction().getBlock(), edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,14 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
|
||||
*/
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
predicate localInstructionTaint(Instruction i1, Instruction i2) {
|
||||
localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
|
||||
247
cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
Normal file
247
cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* Minimal, language-neutral type system for the IR.
|
||||
*/
|
||||
|
||||
private import internal.IRTypeInternal
|
||||
|
||||
private newtype TIRType =
|
||||
TIRVoidType() or
|
||||
TIRUnknownType() or
|
||||
TIRErrorType() { Language::hasErrorType() } or
|
||||
TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or
|
||||
TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or
|
||||
TIRUnsignedIntegerType(int byteSize) { Language::hasUnsignedIntegerType(byteSize) } or
|
||||
TIRFloatingPointType(int byteSize) { Language::hasFloatingPointType(byteSize) } or
|
||||
TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or
|
||||
TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or
|
||||
TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) {
|
||||
Language::hasOpaqueType(tag, byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* The language-neutral type of an IR `Instruction`, `Operand`, or `IRVariable`.
|
||||
* The interface to `IRType` and its subclasses is the same across all languages for which the IR
|
||||
* is supported, so analyses that expect to be used for multiple languages should generally use
|
||||
* `IRType` rather than a language-specific type.
|
||||
*
|
||||
* Many types from the language-specific type system will map to a single canonical `IRType`. Two
|
||||
* types that map to the same `IRType` are considered equivalent by the IR. As an example, in C++,
|
||||
* all pointer types map to the same instance of `IRAddressType`.
|
||||
*/
|
||||
class IRType extends TIRType {
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies this `IRType`. This string is often the same as the
|
||||
* result of `IRType.toString()`, but for some types it may be more verbose to ensure uniqueness.
|
||||
*/
|
||||
string getIdentityString() { result = toString() }
|
||||
|
||||
/**
|
||||
* Gets the size of the type, in bytes, if known.
|
||||
*
|
||||
* This will hold for all `IRType` objects except `IRUnknownType`.
|
||||
*/
|
||||
int getByteSize() { none() }
|
||||
|
||||
/**
|
||||
* Gets a single instance of `LanguageType` that maps to this `IRType`.
|
||||
*/
|
||||
Language::LanguageType getCanonicalLanguageType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An unknown type. Generally used to represent results and operands that access an unknown set of
|
||||
* memory locations, such as the side effects of a function call.
|
||||
*/
|
||||
class IRUnknownType extends IRType, TIRUnknownType {
|
||||
final override string toString() { result = "unknown" }
|
||||
|
||||
final override int getByteSize() { none() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalUnknownType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A void type, which has no values. Used to represent the result type of an instruction that does
|
||||
* not produce a result.
|
||||
*/
|
||||
class IRVoidType extends IRType, TIRVoidType {
|
||||
final override string toString() { result = "void" }
|
||||
|
||||
final override int getByteSize() { result = 0 }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalVoidType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error type. Used when an error in the source code prevents the extractor from determining the
|
||||
* proper type.
|
||||
*/
|
||||
class IRErrorType extends IRType, TIRErrorType {
|
||||
final override string toString() { result = "error" }
|
||||
|
||||
final override int getByteSize() { result = 0 }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalErrorType()
|
||||
}
|
||||
}
|
||||
|
||||
private class IRSizedType extends IRType {
|
||||
int byteSize;
|
||||
|
||||
IRSizedType() {
|
||||
this = TIRBooleanType(byteSize) or
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize) or
|
||||
this = TIRAddressType(byteSize) or
|
||||
this = TIRFunctionAddressType(byteSize) or
|
||||
this = TIROpaqueType(_, byteSize)
|
||||
}
|
||||
|
||||
final override int getByteSize() { result = byteSize }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean type, which can hold the values `true` (non-zero) or `false` (zero).
|
||||
*/
|
||||
class IRBooleanType extends IRSizedType, TIRBooleanType {
|
||||
final override string toString() { result = "bool" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalBooleanType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
|
||||
* `IRFloatingPointType`.
|
||||
*/
|
||||
class IRNumericType extends IRSizedType {
|
||||
IRNumericType() {
|
||||
this = TIRSignedIntegerType(byteSize) or
|
||||
this = TIRUnsignedIntegerType(byteSize) or
|
||||
this = TIRFloatingPointType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
|
||||
* integer, as well as character types whose representation is signed.
|
||||
*/
|
||||
class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
|
||||
final override string toString() { result = "int" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalSignedIntegerType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
|
||||
* unsigned integer, as well as character types whose representation is unsigned.
|
||||
*/
|
||||
class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
|
||||
final override string toString() { result = "uint" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalUnsignedIntegerType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point type.
|
||||
*/
|
||||
class IRFloatingPointType extends IRNumericType, TIRFloatingPointType {
|
||||
final override string toString() { result = "float" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFloatingPointType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An address type, representing the memory address of data. Used to represent pointers, references,
|
||||
* and lvalues, include those that are garbage collected.
|
||||
*
|
||||
* The address of a function is represented by the separate `IRFunctionAddressType`.
|
||||
*/
|
||||
class IRAddressType extends IRSizedType, TIRAddressType {
|
||||
final override string toString() { result = "addr" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalAddressType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An address type, representing the memory address of code. Used to represent function pointers,
|
||||
* function references, and the target of a direct function call.
|
||||
*/
|
||||
class IRFunctionAddressType extends IRSizedType, TIRFunctionAddressType {
|
||||
final override string toString() { result = "func" + byteSize.toString() }
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalFunctionAddressType(byteSize)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type with known size that does not fit any of the other kinds of type. Used to represent
|
||||
* classes, structs, unions, fixed-size arrays, pointers-to-member, and more.
|
||||
*/
|
||||
class IROpaqueType extends IRSizedType, TIROpaqueType {
|
||||
Language::OpaqueTypeTag tag;
|
||||
|
||||
IROpaqueType() { this = TIROpaqueType(tag, byteSize) }
|
||||
|
||||
final override string toString() {
|
||||
result = "opaque" + byteSize.toString() + "{" + tag.toString() + "}"
|
||||
}
|
||||
|
||||
final override string getIdentityString() {
|
||||
result = "opaque" + byteSize.toString() + "{" + Language::getOpaqueTagIdentityString(tag) + "}"
|
||||
}
|
||||
|
||||
final override Language::LanguageType getCanonicalLanguageType() {
|
||||
result = Language::getCanonicalOpaqueType(tag, byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "tag" that differentiates this type from other incompatible opaque types that have the
|
||||
* same size.
|
||||
*/
|
||||
final Language::OpaqueTypeTag getTag() { result = tag }
|
||||
}
|
||||
|
||||
module IRTypeSanity {
|
||||
query predicate missingCanonicalLanguageType(IRType type, string message) {
|
||||
not exists(type.getCanonicalLanguageType()) and
|
||||
message = "Type does not have a canonical `LanguageType`"
|
||||
}
|
||||
|
||||
query predicate multipleCanonicalLanguageTypes(IRType type, string message) {
|
||||
strictcount(type.getCanonicalLanguageType()) > 1 and
|
||||
message = "Type has multiple canonical `LanguageType`s: " +
|
||||
concat(type.getCanonicalLanguageType().toString(), ", ")
|
||||
}
|
||||
|
||||
query predicate missingIRType(Language::LanguageType type, string message) {
|
||||
not exists(type.getIRType()) and
|
||||
message = "`LanguageType` does not have a corresponding `IRType`."
|
||||
}
|
||||
|
||||
query predicate multipleIRTypes(Language::LanguageType type, string message) {
|
||||
strictcount(type.getIRType()) > 1 and
|
||||
message = "`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " +
|
||||
concat(type.getIRType().toString(), ", ")
|
||||
}
|
||||
|
||||
import Language::LanguageTypeSanity
|
||||
}
|
||||
@@ -5,6 +5,7 @@ private newtype TMemoryAccessKind =
|
||||
TBufferMayMemoryAccess() or
|
||||
TEscapedMemoryAccess() or
|
||||
TEscapedMayMemoryAccess() or
|
||||
TNonLocalMayMemoryAccess() or
|
||||
TPhiMemoryAccess() or
|
||||
TUnmodeledMemoryAccess() or
|
||||
TChiTotalMemoryAccess() or
|
||||
@@ -80,6 +81,14 @@ class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess {
|
||||
override string toString() { result = "escaped(may)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand or result may access all memory whose address has escaped, other than data on the
|
||||
* stack frame of the current function.
|
||||
*/
|
||||
class NonLocalMayMemoryAccess extends MemoryAccessKind, TNonLocalMayMemoryAccess {
|
||||
override string toString() { result = "nonlocal(may)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The operand is a Phi operand, which accesses the same memory as its
|
||||
* definition.
|
||||
|
||||
@@ -57,6 +57,7 @@ private newtype TOpcode =
|
||||
TUnmodeledDefinition() or
|
||||
TUnmodeledUse() or
|
||||
TAliasedDefinition() or
|
||||
TAliasedUse() or
|
||||
TPhi() or
|
||||
TBuiltIn() or
|
||||
TVarArgsStart() or
|
||||
@@ -393,6 +394,10 @@ module Opcode {
|
||||
final override string toString() { result = "AliasedDefinition" }
|
||||
}
|
||||
|
||||
class AliasedUse extends Opcode, TAliasedUse {
|
||||
final override string toString() { result = "AliasedUse" }
|
||||
}
|
||||
|
||||
class Phi extends Opcode, TPhi {
|
||||
final override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.IRImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
import IRTypeSanity
|
||||
|
||||
@@ -5,6 +5,7 @@ import Imports::TempVariableTag
|
||||
private import Imports::IRUtilities
|
||||
private import Imports::TTempVariableTag
|
||||
private import Imports::TIRVariable
|
||||
private import Imports::IRType
|
||||
|
||||
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
|
||||
result.getVariable() = var and
|
||||
@@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::Type getType();
|
||||
final Language::Type getType() { getLanguageType().hasType(result, false) }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the variable.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::LanguageType getLanguageType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
@@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable {
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRUserVariable() { this = TIRUserVariable(var, type, func) }
|
||||
|
||||
@@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
result = getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
/**
|
||||
* Gets the original user-declared variable.
|
||||
@@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
TempVariableTag tag;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.InstructionImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
@@ -49,7 +50,8 @@ module InstructionSanity {
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -113,10 +115,12 @@ module InstructionSanity {
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func |
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
func = operand.getUse().getEnclosingFunction() and
|
||||
message = "Operand missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString()
|
||||
+ "' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -260,6 +264,7 @@ module InstructionSanity {
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not useOperand.getUse() instanceof UnmodeledUseInstruction and
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
if getResultType() instanceof Language::VoidType
|
||||
if getResultIRType() instanceof IRVoidType
|
||||
then result = "v"
|
||||
else
|
||||
if hasMemoryResult()
|
||||
@@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[type]
|
||||
private string getValueCategoryString(string type) {
|
||||
if isGLValue() then result = "glval<" + type + ">" else result = type
|
||||
}
|
||||
|
||||
string getResultTypeString() {
|
||||
exists(string valcat |
|
||||
valcat = getValueCategoryString(getResultType().toString()) and
|
||||
if
|
||||
getResultType() instanceof Language::UnknownType and
|
||||
not isGLValue() and
|
||||
exists(getResultSize())
|
||||
then result = valcat + "[" + getResultSize().toString() + "]"
|
||||
else result = valcat
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human-readable string that uniquely identifies this instruction
|
||||
* within the function. This string is used to refer to this instruction when
|
||||
@@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction {
|
||||
*
|
||||
* Example: `r1_1(int*)`
|
||||
*/
|
||||
final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" }
|
||||
final string getResultString() {
|
||||
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operands of this instruction, suitable for
|
||||
@@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
result = Construction::getInstructionResultType(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `isGLValue()` holds, then the result type of this instruction should be
|
||||
* thought of as "pointer to `getResultType()`".
|
||||
*/
|
||||
final Language::Type getResultType() { Construction::instructionHasType(this, result, _) }
|
||||
final Language::Type getResultType() {
|
||||
exists(Language::LanguageType resultType |
|
||||
resultType = getResultLanguageType() and
|
||||
(
|
||||
resultType.hasUnspecifiedType(result, _)
|
||||
or
|
||||
not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the result produced by this instruction is a glvalue. If this
|
||||
@@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::instructionHasType(this, _, true) }
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() {
|
||||
if isGLValue()
|
||||
then
|
||||
// a glvalue is always pointer-sized.
|
||||
result = Language::getPointerSize()
|
||||
else
|
||||
if getResultType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionResultSize(this)
|
||||
else result = Language::getTypeSize(getResultType())
|
||||
}
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction {
|
||||
* An instruction that catches an exception of a specific type.
|
||||
*/
|
||||
class CatchByTypeInstruction extends CatchInstruction {
|
||||
Language::Type exceptionType;
|
||||
Language::LanguageType exceptionType;
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
@@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
/**
|
||||
* Gets the type of exception to be caught.
|
||||
*/
|
||||
final Language::Type getExceptionType() { result = exceptionType }
|
||||
final Language::LanguageType getExceptionType() { result = exceptionType }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction {
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that consumes all escaped memory on exit from the function.
|
||||
*/
|
||||
class AliasedUseInstruction extends Instruction {
|
||||
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
private import Instruction
|
||||
private import IRBlock
|
||||
private import internal.OperandImports as Imports
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Overlap
|
||||
private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
@@ -143,22 +144,40 @@ class Operand extends TOperand {
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
Language::Type getType() { result = getAnyDef().getResultType() }
|
||||
Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
|
||||
* as the result type of the definition instruction consumed by this operand. For register
|
||||
* operands, this is always the case. For some memory operands, the operand type may be different
|
||||
* from the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final Language::Type getType() { getLanguageType().hasType(result, _) }
|
||||
|
||||
/**
|
||||
* Holds if the value consumed by this operand is a glvalue. If this
|
||||
* holds, the value of the operand represents the address of a location,
|
||||
* and the type of the location is given by `getType()`. If this does
|
||||
* not hold, the value of the operand represents a value whose type is
|
||||
* given by `getResultType()`.
|
||||
* given by `getType()`.
|
||||
*/
|
||||
predicate isGLValue() { getAnyDef().isGLValue() }
|
||||
final predicate isGLValue() { getLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
int getSize() { result = Language::getTypeSize(getType()) }
|
||||
final int getSize() { result = getLanguageType().getByteSize() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,11 +189,6 @@ class MemoryOperand extends Operand {
|
||||
this = TPhiOperand(_, _, _, _)
|
||||
}
|
||||
|
||||
override predicate isGLValue() {
|
||||
// A `MemoryOperand` can never be a glvalue
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
@@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
|
||||
class TypedOperand extends NonPhiMemoryOperand {
|
||||
override TypedOperandTag tag;
|
||||
|
||||
final override Language::Type getType() {
|
||||
final override Language::LanguageType getLanguageType() {
|
||||
result = Construction::getInstructionOperandType(useInstr, tag)
|
||||
}
|
||||
}
|
||||
@@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
final override int getSize() {
|
||||
if getType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
private import cpp
|
||||
private import internal.ValueNumberingImports
|
||||
private import IR
|
||||
|
||||
/**
|
||||
@@ -23,31 +23,32 @@ newtype TValueNumber =
|
||||
initializeParameterValueNumber(_, irFunc, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
|
||||
TConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
constantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
ValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
@@ -59,7 +60,7 @@ newtype TValueNumber =
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() { result = getExampleInstruction().getResultId() }
|
||||
|
||||
final Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
@@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate stringConstantValueNumber(
|
||||
StringConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue().getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
@@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber(
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
|
||||
int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
|
||||
Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
@@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber(
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultType() instanceof VoidType and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
@@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
stringConstantValueNumber(instr, irFunc, type, value) and
|
||||
result = TStringConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
exists(Language::Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
exists(
|
||||
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
|
|
||||
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
|
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
@@ -1,6 +1,6 @@
|
||||
private import cpp
|
||||
import AliasAnalysis
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
private import semmle.code.cpp.Print
|
||||
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
@@ -10,31 +10,42 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
private predicate hasResultMemoryAccess(
|
||||
Instruction instr, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset
|
||||
Instruction instr, IRVariable var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset
|
||||
) {
|
||||
resultPointsTo(instr.getResultAddress(), var, startBitOffset) and
|
||||
type = instr.getResultType() and
|
||||
if exists(instr.getResultSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(instr.getResultSize(), 8))
|
||||
languageType = instr.getResultLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private predicate hasOperandMemoryAccess(
|
||||
MemoryOperand operand, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset
|
||||
MemoryOperand operand, IRVariable var, IRType type, Language::LanguageType languageType,
|
||||
IntValue startBitOffset, IntValue endBitOffset
|
||||
) {
|
||||
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, startBitOffset) and
|
||||
type = operand.getType() and
|
||||
if exists(operand.getSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(operand.getSize(), 8))
|
||||
languageType = operand.getLanguageType() and
|
||||
type = languageType.getIRType() and
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
private newtype TMemoryLocation =
|
||||
TVariableMemoryLocation(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset) {
|
||||
hasResultMemoryAccess(_, var, type, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, startBitOffset, endBitOffset)
|
||||
TVariableMemoryLocation(
|
||||
IRVariable var, IRType type, Language::LanguageType languageType, IntValue startBitOffset,
|
||||
IntValue endBitOffset
|
||||
) {
|
||||
(
|
||||
hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset)
|
||||
) and
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
TUnknownMemoryLocation(IRFunction irFunc) or
|
||||
TUnknownNonLocalMemoryLocation(IRFunction irFunc) or
|
||||
TUnknownVirtualVariable(IRFunction irFunc)
|
||||
|
||||
/**
|
||||
@@ -49,9 +60,11 @@ abstract class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
abstract VirtualVariable getVirtualVariable();
|
||||
|
||||
abstract Type getType();
|
||||
abstract Language::LanguageType getType();
|
||||
|
||||
abstract string getUniqueId();
|
||||
|
||||
final IRType getIRType() { result = getType().getIRType() }
|
||||
}
|
||||
|
||||
abstract class VirtualVariable extends MemoryLocation { }
|
||||
@@ -62,20 +75,34 @@ abstract class VirtualVariable extends MemoryLocation { }
|
||||
*/
|
||||
class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
IRVariable var;
|
||||
Type type;
|
||||
IRType type;
|
||||
Language::LanguageType languageType;
|
||||
IntValue startBitOffset;
|
||||
IntValue endBitOffset;
|
||||
|
||||
VariableMemoryLocation() {
|
||||
this = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
|
||||
this = TVariableMemoryLocation(var, type, languageType, startBitOffset, endBitOffset)
|
||||
}
|
||||
|
||||
final override string toString() {
|
||||
result = var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
|
||||
type.toString() + ">"
|
||||
type.toString() + ", " + languageType.toString() + ">"
|
||||
}
|
||||
|
||||
final override Type getType() { result = type }
|
||||
final override Language::LanguageType getType() {
|
||||
if
|
||||
strictcount(Language::LanguageType accessType |
|
||||
hasResultMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset)
|
||||
) = 1
|
||||
then
|
||||
// All of the accesses have the same `LanguageType`, so just use that.
|
||||
hasResultMemoryAccess(_, var, type, result, startBitOffset, endBitOffset) or
|
||||
hasOperandMemoryAccess(_, var, type, result, startBitOffset, endBitOffset)
|
||||
else
|
||||
// There is no single type for all accesses, so just use the canonical one for this `IRType`.
|
||||
result = type.getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final IntValue getStartBitOffset() { result = startBitOffset }
|
||||
|
||||
@@ -85,13 +112,14 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = var.getUniqueId() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
|
||||
getTypeIdentityString(type) + ">"
|
||||
type.getIdentityString() + ">"
|
||||
}
|
||||
|
||||
final override VirtualVariable getVirtualVariable() {
|
||||
if variableAddressEscapes(var)
|
||||
then result = TUnknownVirtualVariable(var.getEnclosingIRFunction())
|
||||
else result = TVariableMemoryLocation(var, var.getType(), 0, var.getType().getSize() * 8)
|
||||
else
|
||||
result = TVariableMemoryLocation(var, var.getIRType(), _, 0, var.getIRType().getByteSize() * 8)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +127,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
*/
|
||||
final predicate coversEntireVariable() {
|
||||
startBitOffset = 0 and
|
||||
endBitOffset = var.getType().getSize() * 8
|
||||
endBitOffset = var.getIRType().getByteSize() * 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +139,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
|
||||
class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable {
|
||||
VariableVirtualVariable() {
|
||||
not variableAddressEscapes(var) and
|
||||
type = var.getType() and
|
||||
type = var.getIRType() and
|
||||
coversEntireVariable()
|
||||
}
|
||||
}
|
||||
@@ -128,11 +156,33 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
|
||||
|
||||
final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) }
|
||||
|
||||
final override Type getType() { result instanceof UnknownType }
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = "{Unknown}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to memory that is not known to be confined to a specific `IRVariable`, but is known to
|
||||
* not access memory on the current function's stack frame.
|
||||
*/
|
||||
class UnknownNonLocalMemoryLocation extends TUnknownNonLocalMemoryLocation, MemoryLocation {
|
||||
IRFunction irFunc;
|
||||
|
||||
UnknownNonLocalMemoryLocation() { this = TUnknownNonLocalMemoryLocation(irFunc) }
|
||||
|
||||
final override string toString() { result = "{UnknownNonLocal}" }
|
||||
|
||||
final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) }
|
||||
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = "{UnknownNonLocal}" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to all aliased memory.
|
||||
*/
|
||||
@@ -143,7 +193,9 @@ class UnknownVirtualVariable extends TUnknownVirtualVariable, VirtualVariable {
|
||||
|
||||
final override string toString() { result = "{AllAliased}" }
|
||||
|
||||
final override Type getType() { result instanceof UnknownType }
|
||||
final override Language::LanguageType getType() {
|
||||
result = any(IRUnknownType type).getCanonicalLanguageType()
|
||||
}
|
||||
|
||||
final override string getUniqueId() { result = " " + toString() }
|
||||
|
||||
@@ -163,6 +215,13 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
def instanceof UnknownMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
// An UnknownNonLocalMemoryLocation may partially overlap any location within the same virtual
|
||||
// variable, except a local variable.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
def instanceof UnknownNonLocalMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap and
|
||||
not use.(VariableMemoryLocation).getVariable() instanceof IRAutomaticVariable
|
||||
or
|
||||
exists(VariableMemoryLocation defVariableLocation |
|
||||
defVariableLocation = def and
|
||||
(
|
||||
@@ -171,13 +230,20 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
(use instanceof UnknownMemoryLocation or use instanceof UnknownVirtualVariable) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
or
|
||||
// A VariableMemoryLocation that is not a local variable may partially overlap an unknown
|
||||
// non-local location within the same virtual variable.
|
||||
def.getVirtualVariable() = use.getVirtualVariable() and
|
||||
use instanceof UnknownNonLocalMemoryLocation and
|
||||
result instanceof MayPartiallyOverlap and
|
||||
not defVariableLocation.getVariable() instanceof IRAutomaticVariable
|
||||
or
|
||||
// A VariableMemoryLocation overlaps another location within the same variable based on the relationship
|
||||
// of the two offset intervals.
|
||||
exists(Overlap intervalOverlap |
|
||||
intervalOverlap = getVariableMemoryLocationOverlap(def, use) and
|
||||
if intervalOverlap instanceof MustExactlyOverlap
|
||||
then
|
||||
if def.getType() = use.getType()
|
||||
if def.getIRType() = use.getIRType()
|
||||
then
|
||||
// The def and use types match, so it's an exact overlap.
|
||||
result instanceof MustExactlyOverlap
|
||||
@@ -282,11 +348,11 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
(
|
||||
(
|
||||
kind.usesAddressOperand() and
|
||||
if hasResultMemoryAccess(instr, _, _, _, _)
|
||||
if hasResultMemoryAccess(instr, _, _, _, _, _)
|
||||
then
|
||||
exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
|
||||
exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction())
|
||||
)
|
||||
@@ -296,6 +362,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
|
||||
or
|
||||
kind instanceof EscapedMayMemoryAccess and
|
||||
result = TUnknownMemoryLocation(instr.getEnclosingIRFunction())
|
||||
or
|
||||
kind instanceof NonLocalMayMemoryAccess and
|
||||
result = TUnknownNonLocalMemoryLocation(instr.getEnclosingIRFunction())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -306,11 +375,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
(
|
||||
(
|
||||
kind.usesAddressOperand() and
|
||||
if hasOperandMemoryAccess(operand, _, _, _, _)
|
||||
if hasOperandMemoryAccess(operand, _, _, _, _, _)
|
||||
then
|
||||
exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
|
||||
exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset) and
|
||||
result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset)
|
||||
)
|
||||
else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction())
|
||||
)
|
||||
@@ -320,6 +389,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
|
||||
or
|
||||
kind instanceof EscapedMayMemoryAccess and
|
||||
result = TUnknownMemoryLocation(operand.getEnclosingIRFunction())
|
||||
or
|
||||
kind instanceof NonLocalMayMemoryAccess and
|
||||
result = TUnknownNonLocalMemoryLocation(operand.getEnclosingIRFunction())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag
|
||||
import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities
|
||||
import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -4,34 +4,52 @@ private import Alias
|
||||
private import SSAConstruction
|
||||
private import DebugSSA
|
||||
|
||||
bindingset[offset]
|
||||
private string getKeySuffixForOffset(int offset) {
|
||||
if offset % 2 = 0 then result = "" else result = "_Chi"
|
||||
}
|
||||
|
||||
bindingset[offset]
|
||||
private int getIndexForOffset(int offset) { result = offset / 2 }
|
||||
|
||||
/**
|
||||
* Property provide that dumps the memory access of each result. Useful for debugging SSA
|
||||
* construction.
|
||||
*/
|
||||
class PropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
exists(MemoryLocation location |
|
||||
location = getResultMemoryLocation(instruction) and
|
||||
(
|
||||
key = "ResultMemoryLocation" and result = location.toString()
|
||||
or
|
||||
key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString()
|
||||
key = "ResultMemoryLocation" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getResultMemoryLocation(instruction)
|
||||
|
|
||||
loc.toString(), ","
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation location |
|
||||
location = getOperandMemoryLocation(instruction.getAnOperand()) and
|
||||
(
|
||||
key = "OperandMemoryAccess" and result = location.toString()
|
||||
or
|
||||
key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString()
|
||||
key = "ResultVirtualVariable" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getResultMemoryLocation(instruction)
|
||||
|
|
||||
loc.getVirtualVariable().toString(), ","
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
|
||||
defBlock.getInstruction(defIndex) = instruction and
|
||||
key = "DefinitionRank[" + useLocation.toString() + "]" and
|
||||
key = "OperandMemoryLocation" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getOperandMemoryLocation(instruction.getAnOperand())
|
||||
|
|
||||
loc.toString(), ","
|
||||
)
|
||||
or
|
||||
key = "OperandVirtualVariable" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getOperandMemoryLocation(instruction.getAnOperand())
|
||||
|
|
||||
loc.getVirtualVariable().toString(), ","
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
|
||||
defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
|
||||
key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and
|
||||
result = defRank.toString()
|
||||
)
|
||||
or
|
||||
@@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider {
|
||||
result = useRank.toString()
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
|
||||
defBlock.getInstruction(defIndex) = instruction and
|
||||
key = "DefinitionReachesUse[" + useLocation.toString() + "]" and
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
|
||||
defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
|
||||
key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString()
|
||||
+ "]" and
|
||||
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
|
||||
exists(Instruction useInstruction |
|
||||
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import SSAConstructionInternal
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import SSAConstructionImports
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -18,7 +15,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Function func) {
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
@@ -42,7 +39,7 @@ private module Cached {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Function function) {
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
@@ -50,12 +47,14 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) {
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getType() = type
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,24 +134,12 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
|
||||
Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instr) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
result = oldOperand.getType()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instr) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
// Only return a result for operands that need an explicit result size.
|
||||
oldOperand.getType() instanceof UnknownType and
|
||||
result = oldOperand.getSize()
|
||||
result = oldOperand.getLanguageType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -196,20 +183,21 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction
|
||||
*/
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
@@ -252,7 +240,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Locatable getInstructionAST(Instruction instruction) {
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
@@ -270,29 +258,25 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) {
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
type = oldInstruction.getResultType() and
|
||||
if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
type = vvar.getType() and
|
||||
isGLValue = false
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
type = location.getType() and
|
||||
isGLValue = false
|
||||
result = location.getType()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
type instanceof VoidType and
|
||||
isGLValue = false
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -338,7 +322,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
@@ -348,7 +332,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Function getInstructionFunction(Instruction instruction) {
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
@@ -358,19 +342,19 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
Language::StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result = getOldInstruction(instruction)
|
||||
.(OldIR::BuiltInOperationInstruction)
|
||||
.getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionExceptionType(Instruction instruction) {
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
@@ -380,14 +364,9 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
// Only return a result for instructions that needed an explicit result size.
|
||||
instruction.getResultType() instanceof UnknownType and
|
||||
result = getOldInstruction(instruction).getResultSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
@@ -2,4 +2,5 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import AliasedSSA as Alias
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
@@ -2,11 +2,11 @@ private import TIRVariableInternal
|
||||
private import Imports::TempVariableTag
|
||||
|
||||
newtype TIRVariable =
|
||||
TIRUserVariable(Language::Variable var, Language::Type type, Language::Function func) {
|
||||
TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Function func) {
|
||||
Construction::hasUserVariable(func, var, type)
|
||||
} or
|
||||
TIRTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::Type type
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
Construction::hasTempVariable(func, ast, tag, type)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.IRImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
import IRTypeSanity
|
||||
|
||||
@@ -5,6 +5,7 @@ import Imports::TempVariableTag
|
||||
private import Imports::IRUtilities
|
||||
private import Imports::TTempVariableTag
|
||||
private import Imports::TIRVariable
|
||||
private import Imports::IRType
|
||||
|
||||
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
|
||||
result.getVariable() = var and
|
||||
@@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::Type getType();
|
||||
final Language::Type getType() { getLanguageType().hasType(result, false) }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the variable.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::LanguageType getLanguageType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
@@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable {
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRUserVariable() { this = TIRUserVariable(var, type, func) }
|
||||
|
||||
@@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
result = getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
/**
|
||||
* Gets the original user-declared variable.
|
||||
@@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
TempVariableTag tag;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.InstructionImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
@@ -49,7 +50,8 @@ module InstructionSanity {
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -113,10 +115,12 @@ module InstructionSanity {
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func |
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
func = operand.getUse().getEnclosingFunction() and
|
||||
message = "Operand missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString()
|
||||
+ "' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -260,6 +264,7 @@ module InstructionSanity {
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not useOperand.getUse() instanceof UnmodeledUseInstruction and
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
if getResultType() instanceof Language::VoidType
|
||||
if getResultIRType() instanceof IRVoidType
|
||||
then result = "v"
|
||||
else
|
||||
if hasMemoryResult()
|
||||
@@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[type]
|
||||
private string getValueCategoryString(string type) {
|
||||
if isGLValue() then result = "glval<" + type + ">" else result = type
|
||||
}
|
||||
|
||||
string getResultTypeString() {
|
||||
exists(string valcat |
|
||||
valcat = getValueCategoryString(getResultType().toString()) and
|
||||
if
|
||||
getResultType() instanceof Language::UnknownType and
|
||||
not isGLValue() and
|
||||
exists(getResultSize())
|
||||
then result = valcat + "[" + getResultSize().toString() + "]"
|
||||
else result = valcat
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human-readable string that uniquely identifies this instruction
|
||||
* within the function. This string is used to refer to this instruction when
|
||||
@@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction {
|
||||
*
|
||||
* Example: `r1_1(int*)`
|
||||
*/
|
||||
final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" }
|
||||
final string getResultString() {
|
||||
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operands of this instruction, suitable for
|
||||
@@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
result = Construction::getInstructionResultType(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `isGLValue()` holds, then the result type of this instruction should be
|
||||
* thought of as "pointer to `getResultType()`".
|
||||
*/
|
||||
final Language::Type getResultType() { Construction::instructionHasType(this, result, _) }
|
||||
final Language::Type getResultType() {
|
||||
exists(Language::LanguageType resultType |
|
||||
resultType = getResultLanguageType() and
|
||||
(
|
||||
resultType.hasUnspecifiedType(result, _)
|
||||
or
|
||||
not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the result produced by this instruction is a glvalue. If this
|
||||
@@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::instructionHasType(this, _, true) }
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() {
|
||||
if isGLValue()
|
||||
then
|
||||
// a glvalue is always pointer-sized.
|
||||
result = Language::getPointerSize()
|
||||
else
|
||||
if getResultType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionResultSize(this)
|
||||
else result = Language::getTypeSize(getResultType())
|
||||
}
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction {
|
||||
* An instruction that catches an exception of a specific type.
|
||||
*/
|
||||
class CatchByTypeInstruction extends CatchInstruction {
|
||||
Language::Type exceptionType;
|
||||
Language::LanguageType exceptionType;
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
@@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
/**
|
||||
* Gets the type of exception to be caught.
|
||||
*/
|
||||
final Language::Type getExceptionType() { result = exceptionType }
|
||||
final Language::LanguageType getExceptionType() { result = exceptionType }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction {
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that consumes all escaped memory on exit from the function.
|
||||
*/
|
||||
class AliasedUseInstruction extends Instruction {
|
||||
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
private import Instruction
|
||||
private import IRBlock
|
||||
private import internal.OperandImports as Imports
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Overlap
|
||||
private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
@@ -143,22 +144,40 @@ class Operand extends TOperand {
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
Language::Type getType() { result = getAnyDef().getResultType() }
|
||||
Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
|
||||
* as the result type of the definition instruction consumed by this operand. For register
|
||||
* operands, this is always the case. For some memory operands, the operand type may be different
|
||||
* from the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final Language::Type getType() { getLanguageType().hasType(result, _) }
|
||||
|
||||
/**
|
||||
* Holds if the value consumed by this operand is a glvalue. If this
|
||||
* holds, the value of the operand represents the address of a location,
|
||||
* and the type of the location is given by `getType()`. If this does
|
||||
* not hold, the value of the operand represents a value whose type is
|
||||
* given by `getResultType()`.
|
||||
* given by `getType()`.
|
||||
*/
|
||||
predicate isGLValue() { getAnyDef().isGLValue() }
|
||||
final predicate isGLValue() { getLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
int getSize() { result = Language::getTypeSize(getType()) }
|
||||
final int getSize() { result = getLanguageType().getByteSize() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,11 +189,6 @@ class MemoryOperand extends Operand {
|
||||
this = TPhiOperand(_, _, _, _)
|
||||
}
|
||||
|
||||
override predicate isGLValue() {
|
||||
// A `MemoryOperand` can never be a glvalue
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
@@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
|
||||
class TypedOperand extends NonPhiMemoryOperand {
|
||||
override TypedOperandTag tag;
|
||||
|
||||
final override Language::Type getType() {
|
||||
final override Language::LanguageType getLanguageType() {
|
||||
result = Construction::getInstructionOperandType(useInstr, tag)
|
||||
}
|
||||
}
|
||||
@@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
final override int getSize() {
|
||||
if getType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
private import cpp
|
||||
private import internal.ValueNumberingImports
|
||||
private import IR
|
||||
|
||||
/**
|
||||
@@ -23,31 +23,32 @@ newtype TValueNumber =
|
||||
initializeParameterValueNumber(_, irFunc, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
|
||||
TConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
constantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
ValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
@@ -59,7 +60,7 @@ newtype TValueNumber =
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() { result = getExampleInstruction().getResultId() }
|
||||
|
||||
final Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
@@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate stringConstantValueNumber(
|
||||
StringConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue().getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
@@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber(
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
|
||||
int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
|
||||
Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
@@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber(
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultType() instanceof VoidType and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
@@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
stringConstantValueNumber(instr, irFunc, type, value) and
|
||||
result = TStringConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
exists(Language::Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
exists(
|
||||
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
|
|
||||
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
|
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
@@ -1,6 +1,8 @@
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
@@ -25,16 +27,16 @@ private module Cached {
|
||||
cached
|
||||
newtype TInstruction =
|
||||
MkInstruction(TranslatedElement element, InstructionTag tag) {
|
||||
element.hasInstruction(_, tag, _, _)
|
||||
element.hasInstruction(_, tag, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasUserVariable(Function func, Variable var, Type type) {
|
||||
predicate hasUserVariable(Function func, Variable var, CppType type) {
|
||||
getTranslatedFunction(func).hasUserVariable(var, type)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) {
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
|
||||
exists(TranslatedElement element |
|
||||
element.getAST() = ast and
|
||||
func = element.getFunction() and
|
||||
@@ -85,22 +87,16 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
|
||||
CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
|
||||
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
|
||||
// the result type of the load.
|
||||
result = instruction.(LoadInstruction).getResultType()
|
||||
result = instruction.(LoadInstruction).getResultLanguageType()
|
||||
or
|
||||
not instruction instanceof LoadInstruction and
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
.getInstructionOperandType(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionOperandSize(Instruction instruction, SideEffectOperandTag tag) {
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
.getInstructionOperandSize(getInstructionTag(instruction), tag)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getPhiOperandDefinition(
|
||||
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
|
||||
@@ -220,15 +216,15 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) {
|
||||
CppType getInstructionResultType(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(_, getInstructionTag(instruction), type, isGLValue)
|
||||
.hasInstruction(_, getInstructionTag(instruction), result)
|
||||
}
|
||||
|
||||
cached
|
||||
Opcode getInstructionOpcode(Instruction instruction) {
|
||||
getInstructionTranslatedElement(instruction)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _, _)
|
||||
.hasInstruction(result, getInstructionTag(instruction), _)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -283,7 +279,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionExceptionType(Instruction instruction) {
|
||||
CppType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getInstructionTranslatedElement(instruction)
|
||||
.getInstructionExceptionType(getInstructionTag(instruction))
|
||||
}
|
||||
@@ -310,6 +306,11 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate needsUnknownOpaqueType(int byteSize) {
|
||||
exists(TranslatedElement element | element.needsUnknownOpaqueType(byteSize))
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
exists(TranslatedElement element, InstructionTag tag |
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag
|
||||
import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities
|
||||
import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -26,6 +26,7 @@ newtype TInstructionTag =
|
||||
UnmodeledDefinitionTag() or
|
||||
UnmodeledUseTag() or
|
||||
AliasedDefinitionTag() or
|
||||
AliasedUseTag() or
|
||||
SwitchBranchTag() or
|
||||
CallTargetTag() or
|
||||
CallTag() or
|
||||
@@ -119,6 +120,8 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
or
|
||||
tag = AliasedDefinitionTag() and result = "AliasedDef"
|
||||
or
|
||||
tag = AliasedUseTag() and result = "AliasedUse"
|
||||
or
|
||||
tag = SwitchBranchTag() and result = "SwitchBranch"
|
||||
or
|
||||
tag = CallTargetTag() and result = "CallTarget"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
@@ -33,13 +34,10 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
else result = getFirstCallTargetInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Call and
|
||||
resultType = getCallResultType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(getCallResultType())
|
||||
or
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
@@ -47,13 +45,12 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
if hasWriteSideEffect()
|
||||
then (
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType instanceof UnknownType
|
||||
resultType = getUnknownType()
|
||||
) else (
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
resultType instanceof VoidType
|
||||
resultType = getVoidType()
|
||||
)
|
||||
) and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
@@ -118,11 +115,11 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
}
|
||||
|
||||
final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag = CallSideEffectTag() and
|
||||
hasSideEffect() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result instanceof UnknownType
|
||||
result = getUnknownType()
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(CallTag()) }
|
||||
@@ -224,18 +221,12 @@ abstract class TranslatedDirectCall extends TranslatedCall {
|
||||
|
||||
final override Instruction getCallTargetResult() { result = getInstruction(CallTargetTag()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue)
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
TranslatedCall.super.hasInstruction(opcode, tag, resultType)
|
||||
or
|
||||
tag = CallTargetTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
// The database does not contain a `FunctionType` for a function unless
|
||||
// its address was taken, so we'll just use glval<Unknown> instead of
|
||||
// glval<FunctionType>.
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = true
|
||||
resultType = getFunctionGLValueType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -253,7 +244,7 @@ abstract class TranslatedDirectCall extends TranslatedCall {
|
||||
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedCall {
|
||||
override Call expr;
|
||||
|
||||
final override Type getCallResultType() { result = getResultType() }
|
||||
final override Type getCallResultType() { result = expr.getType() }
|
||||
|
||||
final override predicate hasArguments() { exists(expr.getArgument(0)) }
|
||||
|
||||
@@ -349,9 +340,7 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) {
|
||||
none()
|
||||
}
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
|
||||
|
||||
@@ -359,7 +348,9 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
|
||||
|
||||
override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
|
||||
override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
@@ -406,34 +397,30 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
isWrite() and
|
||||
hasSpecificWriteSideEffect(opcode) and
|
||||
tag = OnlyInstructionTag() and
|
||||
(
|
||||
opcode instanceof BufferAccessOpcode and
|
||||
t instanceof UnknownType
|
||||
type = getUnknownType()
|
||||
or
|
||||
not opcode instanceof BufferAccessOpcode and
|
||||
(
|
||||
t = arg.getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
not t instanceof VoidType
|
||||
or
|
||||
arg.getUnspecifiedType().(DerivedType).getBaseType() instanceof VoidType and
|
||||
t instanceof UnknownType
|
||||
exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() |
|
||||
if baseType instanceof VoidType
|
||||
then type = getUnknownType()
|
||||
else type = getTypeForPRValueOrUnknown(baseType)
|
||||
)
|
||||
or
|
||||
index = -1 and
|
||||
not arg.getUnspecifiedType() instanceof DerivedType and
|
||||
t = arg.getUnspecifiedType()
|
||||
) and
|
||||
isGLValue = false
|
||||
type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
not isWrite() and
|
||||
hasSpecificReadSideEffect(opcode) and
|
||||
tag = OnlyInstructionTag() and
|
||||
t instanceof VoidType and
|
||||
isGLValue = false
|
||||
type = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -459,15 +446,27 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
||||
.getFullyConverted()).getResult()
|
||||
}
|
||||
|
||||
override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
result = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
result = arg.getType().getUnspecifiedType() and
|
||||
not result instanceof DerivedType and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
if hasSpecificReadSideEffect(any(Opcode::BufferReadSideEffect op))
|
||||
then
|
||||
result = getUnknownType() and
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
else
|
||||
exists(Type operandType |
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType() and
|
||||
not operandType instanceof DerivedType and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
|
|
||||
// If the type we select is an incomplete type (e.g. a forward-declared `struct`), there will
|
||||
// not be a `CppType` that represents that type. In that case, fall back to `UnknownCppType`.
|
||||
result = getTypeForPRValueOrUnknown(operandType)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasSpecificWriteSideEffect(Opcode op) {
|
||||
@@ -517,7 +516,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
||||
)
|
||||
or
|
||||
not call.getTarget() instanceof SideEffectFunction and
|
||||
op instanceof Opcode::IndirectReadSideEffect
|
||||
op instanceof Opcode::BufferReadSideEffect
|
||||
}
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -37,9 +38,7 @@ abstract class TranslatedFlexibleCondition extends TranslatedCondition, Conditio
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -105,9 +104,7 @@ abstract class TranslatedBinaryLogicalOperation extends TranslatedNativeConditio
|
||||
result = getLeftOperand().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -163,13 +160,10 @@ class TranslatedValueCondition extends TranslatedCondition, TTranslatedValueCond
|
||||
|
||||
override Instruction getFirstInstruction() { result = getValueExpr().getFirstInstruction() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = ValueConditionConditionalBranchTag() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.IRUtilities
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.IRUtilities
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -54,19 +55,15 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getVariableType(getVariable()) and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getVariableType(getVariable()))
|
||||
or
|
||||
hasUninitializedInstruction() and
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Uninitialized and
|
||||
resultType = getVariableType(getVariable()) and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(getVariableType(getVariable()))
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.IRConfiguration
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
@@ -12,11 +13,6 @@ private import TranslatedExpr
|
||||
private import IRConstruction
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* Gets the built-in `int` type.
|
||||
*/
|
||||
Type getIntType() { result.(IntType).isImplicitlySigned() }
|
||||
|
||||
/**
|
||||
* Gets the "real" parent of `expr`. This predicate treats conversions as if
|
||||
* they were explicit nodes in the expression tree, rather than as implicit
|
||||
@@ -54,6 +50,9 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
// constant value.
|
||||
isIRConstant(getRealParent(expr))
|
||||
or
|
||||
// Ignore descendants of `__assume` expressions, since we translated these to `NoOp`.
|
||||
getRealParent(expr) instanceof AssumeExpr
|
||||
or
|
||||
// The `DestructorCall` node for a `DestructorFieldDestruction` has a `FieldAccess`
|
||||
// node as its qualifier, but that `FieldAccess` does not have a child of its own.
|
||||
// We'll ignore that `FieldAccess`, and supply the receiver as part of the calling
|
||||
@@ -67,8 +66,8 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
)
|
||||
or
|
||||
// Do not translate input/output variables in GNU asm statements
|
||||
getRealParent(expr) instanceof AsmStmt
|
||||
or
|
||||
// getRealParent(expr) instanceof AsmStmt
|
||||
// or
|
||||
ignoreExprAndDescendants(getRealParent(expr)) // recursive case
|
||||
or
|
||||
// We do not yet translate destructors properly, so for now we ignore any
|
||||
@@ -542,9 +541,7 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
* If the instruction does not return a result, `resultType` should be
|
||||
* `VoidType`.
|
||||
*/
|
||||
abstract predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
);
|
||||
abstract predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType);
|
||||
|
||||
/**
|
||||
* Gets the `Function` that contains this element.
|
||||
@@ -584,7 +581,7 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
* `tag` must be unique for each variable generated from the same AST node
|
||||
* (not just from the same `TranslatedElement`).
|
||||
*/
|
||||
predicate hasTempVariable(TempVariableTag tag, Type type) { none() }
|
||||
predicate hasTempVariable(TempVariableTag tag, CppType type) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `FunctionInstruction`, gets the
|
||||
@@ -629,6 +626,8 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
*/
|
||||
int getInstructionResultSize(InstructionTag tag) { none() }
|
||||
|
||||
predicate needsUnknownOpaqueType(int byteSize) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is a `StringConstantInstruction`,
|
||||
* gets the `StringLiteral` for that instruction.
|
||||
@@ -644,7 +643,7 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
* If the instruction specified by `tag` is a `CatchByTypeInstruction`,
|
||||
* gets the type of the exception to be caught.
|
||||
*/
|
||||
Type getInstructionExceptionType(InstructionTag tag) { none() }
|
||||
CppType getInstructionExceptionType(InstructionTag tag) { none() }
|
||||
|
||||
/**
|
||||
* If the instruction specified by `tag` is an `InheritanceConversionInstruction`,
|
||||
@@ -663,7 +662,7 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
/**
|
||||
* Gets the type of the memory operand specified by `operandTag` on the the instruction specified by `tag`.
|
||||
*/
|
||||
Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
|
||||
CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
|
||||
|
||||
/**
|
||||
* Gets the size of the memory operand specified by `operandTag` on the the instruction specified by `tag`.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
@@ -51,10 +53,21 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
*/
|
||||
abstract predicate producesExprResult();
|
||||
|
||||
final CppType getResultType() {
|
||||
if isResultGLValue()
|
||||
then result = getTypeForGLValue(expr.getType())
|
||||
else result = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this expression.
|
||||
* Holds if the result of this `TranslatedExpr` is a glvalue.
|
||||
*/
|
||||
final Type getResultType() { result = expr.getUnspecifiedType() }
|
||||
predicate isResultGLValue() {
|
||||
// This implementation is overridden in `TranslatedCoreExpr` to mark them
|
||||
// as glvalues if they have loads on them. It's not overridden in
|
||||
// `TranslatedResultCopy` since result copies never have loads.
|
||||
expr.isGLValueCategory()
|
||||
}
|
||||
|
||||
final override Locatable getAST() { result = expr }
|
||||
|
||||
@@ -82,6 +95,22 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
abstract class TranslatedCoreExpr extends TranslatedExpr {
|
||||
final override string toString() { result = expr.toString() }
|
||||
|
||||
/**
|
||||
* Holds if the result of this `TranslatedExpr` is a glvalue.
|
||||
*/
|
||||
override predicate isResultGLValue() {
|
||||
super.isResultGLValue()
|
||||
or
|
||||
// If this TranslatedExpr doesn't produce the result, then it must represent
|
||||
// a glvalue that is then loaded by a TranslatedLoad.
|
||||
hasLoad()
|
||||
}
|
||||
|
||||
final predicate hasLoad() {
|
||||
expr.hasLValueToRValueConversion() and
|
||||
not ignoreLoad(expr)
|
||||
}
|
||||
|
||||
final override predicate producesExprResult() {
|
||||
// If there's no load, then this is the only TranslatedExpr for this
|
||||
// expression.
|
||||
@@ -89,30 +118,6 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
|
||||
// If there's a result copy, then this expression's result is the copy.
|
||||
not exprNeedsCopyIfNotLoaded(expr)
|
||||
}
|
||||
|
||||
private predicate hasLoad() {
|
||||
expr.hasLValueToRValueConversion() and
|
||||
not ignoreLoad(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the result of this `TranslatedExpr` is a glvalue, or
|
||||
* `false` if the result is a prvalue.
|
||||
*
|
||||
* This predicate returns a `boolean` value instead of just a being a plain
|
||||
* predicate because all of the subclass predicates that call it require a
|
||||
* `boolean` value.
|
||||
*/
|
||||
final boolean isResultGLValue() {
|
||||
if
|
||||
expr.isGLValueCategory()
|
||||
or
|
||||
// If this TranslatedExpr doesn't produce the result, then it must represent
|
||||
// a glvalue that is then loaded by a TranslatedLoad.
|
||||
hasLoad()
|
||||
then result = true
|
||||
else result = false
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
@@ -123,38 +128,32 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
|
||||
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
(
|
||||
tag = ConditionValueTrueTempAddressTag() or
|
||||
tag = ConditionValueFalseTempAddressTag() or
|
||||
tag = ConditionValueResultTempAddressTag()
|
||||
) and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(expr.getType())
|
||||
or
|
||||
(
|
||||
tag = ConditionValueTrueConstantTag() or
|
||||
tag = ConditionValueFalseConstantTag()
|
||||
) and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = getResultType()
|
||||
or
|
||||
(
|
||||
tag = ConditionValueTrueStoreTag() or
|
||||
tag = ConditionValueFalseStoreTag()
|
||||
) and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = getResultType()
|
||||
or
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -215,9 +214,9 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
tag = ConditionValueTempVar() and
|
||||
type = getResultType()
|
||||
type = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
@@ -265,13 +264,10 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = expr.getUnspecifiedType() and
|
||||
if expr.isGLValueCategory() then isGLValue = true else isGLValue = false
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -318,13 +314,10 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = ResultCopyTag() and
|
||||
opcode instanceof Opcode::CopyValue and
|
||||
resultType = getOperand().getResultType() and
|
||||
isGLValue = getOperand().isResultGLValue()
|
||||
resultType = getOperand().getResultType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -372,9 +365,7 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr {
|
||||
child = getRightOperand() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -389,6 +380,10 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr {
|
||||
}
|
||||
}
|
||||
|
||||
private int getElementSize(Type type) {
|
||||
result = max(type.getUnspecifiedType().(PointerType).getBaseType().getSize())
|
||||
}
|
||||
|
||||
abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
override CrementOperation expr;
|
||||
|
||||
@@ -397,9 +392,9 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
final override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = CrementConstantTag() and
|
||||
exists(Type resultType |
|
||||
resultType = getResultType() and
|
||||
resultType = expr.getUnspecifiedType() and
|
||||
(
|
||||
resultType instanceof IntegralType and result = "1"
|
||||
resultType instanceof IntegralOrEnumType and result = "1"
|
||||
or
|
||||
resultType instanceof FloatingPointType and result = "1.0"
|
||||
or
|
||||
@@ -408,38 +403,34 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
)
|
||||
}
|
||||
|
||||
private Type getConstantType() {
|
||||
private CppType getConstantType() {
|
||||
exists(Type resultType |
|
||||
resultType = getResultType() and
|
||||
resultType = expr.getUnspecifiedType() and
|
||||
(
|
||||
resultType instanceof ArithmeticType and result = resultType
|
||||
resultType instanceof ArithmeticType and
|
||||
result = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
resultType instanceof PointerType and result = getIntType()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
isGLValue = false and
|
||||
(
|
||||
tag = CrementLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getResultType()
|
||||
or
|
||||
tag = CrementConstantTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getConstantType()
|
||||
or
|
||||
tag = CrementOpTag() and
|
||||
opcode = getOpcode() and
|
||||
resultType = getResultType()
|
||||
or
|
||||
tag = CrementStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getResultType()
|
||||
)
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = CrementLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
tag = CrementConstantTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getConstantType()
|
||||
or
|
||||
tag = CrementOpTag() and
|
||||
opcode = getOpcode() and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
tag = CrementStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -500,7 +491,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
getOpcode() instanceof Opcode::PointerAdd or
|
||||
getOpcode() instanceof Opcode::PointerSub
|
||||
) and
|
||||
result = max(getResultType().(PointerType).getBaseType().getSize())
|
||||
result = getElementSize(expr.getType())
|
||||
}
|
||||
|
||||
final TranslatedExpr getOperand() {
|
||||
@@ -509,7 +500,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
|
||||
|
||||
final Opcode getOpcode() {
|
||||
exists(Type resultType |
|
||||
resultType = getResultType() and
|
||||
resultType = expr.getUnspecifiedType() and
|
||||
(
|
||||
(
|
||||
expr instanceof IncrementOperation and
|
||||
@@ -590,13 +581,10 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
|
||||
|
||||
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::PointerAdd and
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(expr.getType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -612,7 +600,7 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
|
||||
|
||||
override int getInstructionElementSize(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = max(getResultType().getSize())
|
||||
result = max(expr.getUnspecifiedType().getSize())
|
||||
}
|
||||
|
||||
private TranslatedExpr getBaseOperand() {
|
||||
@@ -635,9 +623,7 @@ abstract class TranslatedTransparentExpr extends TranslatedNonConstantExpr {
|
||||
child = getOperand() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -688,13 +674,10 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr {
|
||||
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::CopyValue and
|
||||
resultType = expr.getUnspecifiedType() and
|
||||
isGLValue = false
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
|
||||
@@ -755,13 +738,10 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(expr.getType())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
@@ -781,13 +761,10 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::FieldAddress and
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(expr.getType())
|
||||
}
|
||||
|
||||
override Field getInstructionField(InstructionTag tag) {
|
||||
@@ -811,13 +788,10 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::FunctionAddress and
|
||||
resultType = expr.getUnspecifiedType() and
|
||||
isGLValue = true
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
override Function getInstructionFunction(InstructionTag tag) {
|
||||
@@ -859,15 +833,10 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr, TTranslatedVal
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode = getOpcode() and
|
||||
resultType = getResultType() and
|
||||
if expr.isGLValueCategory() or expr.hasLValueToRValueConversion()
|
||||
then isGLValue = true
|
||||
else isGLValue = false
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -913,13 +882,10 @@ abstract class TranslatedSingleInstructionExpr extends TranslatedNonConstantExpr
|
||||
*/
|
||||
abstract Opcode getOpcode();
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
opcode = getOpcode() and
|
||||
tag = OnlyInstructionTag() and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
|
||||
@@ -993,13 +959,10 @@ abstract class TranslatedSingleInstructionConversion extends TranslatedConversio
|
||||
child = getOperand() and result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode = getOpcode() and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
|
||||
@@ -1044,7 +1007,7 @@ class TranslatedDynamicCast extends TranslatedSingleInstructionConversion {
|
||||
|
||||
override Opcode getOpcode() {
|
||||
exists(Type resultType |
|
||||
resultType = getResultType() and
|
||||
resultType = expr.getUnspecifiedType() and
|
||||
if resultType instanceof PointerType
|
||||
then
|
||||
if resultType.(PointerType).getBaseType() instanceof VoidType
|
||||
@@ -1102,19 +1065,14 @@ class TranslatedBoolConversion extends TranslatedConversion {
|
||||
child = getOperand() and result = getInstruction(BoolConversionConstantTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
isGLValue = false and
|
||||
(
|
||||
tag = BoolConversionConstantTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getOperand().getResultType()
|
||||
or
|
||||
tag = BoolConversionCompareTag() and
|
||||
opcode instanceof Opcode::CompareNE and
|
||||
resultType instanceof BoolType
|
||||
)
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = BoolConversionConstantTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getOperand().getResultType()
|
||||
or
|
||||
tag = BoolConversionCompareTag() and
|
||||
opcode instanceof Opcode::CompareNE and
|
||||
resultType = getBoolType()
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = getInstruction(BoolConversionCompareTag()) }
|
||||
@@ -1245,7 +1203,7 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
|
||||
opcode instanceof Opcode::PointerSub or
|
||||
opcode instanceof Opcode::PointerDiff
|
||||
) and
|
||||
result = max(getPointerOperand().getResultType().(PointerType).getBaseType().getSize())
|
||||
result = getElementSize(getPointerOperand().getExpr().getType())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1330,13 +1288,10 @@ class TranslatedAssignExpr extends TranslatedAssignment {
|
||||
result = getInstruction(AssignmentStoreTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = AssignmentStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(expr.getType()) // Always a prvalue
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -1413,14 +1368,16 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
// anyway. If we really want to model this case perfectly, we'll need the
|
||||
// extractor to tell us what the promoted type of the left operand would
|
||||
// be.
|
||||
result = getLeftOperand().getResultType()
|
||||
result = getLeftOperand().getExpr().getType()
|
||||
else
|
||||
// The right operand has already been converted to the type of the op.
|
||||
result = getRightOperand().getResultType()
|
||||
result = getRightOperand().getExpr().getType()
|
||||
}
|
||||
|
||||
private predicate leftOperandNeedsConversion() {
|
||||
getConvertedLeftOperandType() != getLeftOperand().getResultType()
|
||||
getConvertedLeftOperandType().getUnspecifiedType() != getLeftOperand()
|
||||
.getExpr()
|
||||
.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private Opcode getOpcode() {
|
||||
@@ -1449,32 +1406,27 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
expr instanceof AssignPointerSubExpr and result instanceof Opcode::PointerSub
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
isGLValue = false and
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = AssignOperationLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
|
||||
or
|
||||
tag = AssignOperationOpTag() and
|
||||
opcode = getOpcode() and
|
||||
resultType = getTypeForPRValue(getConvertedLeftOperandType())
|
||||
or
|
||||
tag = AssignmentStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(expr.getType()) // Always a prvalue
|
||||
or
|
||||
leftOperandNeedsConversion() and
|
||||
opcode instanceof Opcode::Convert and
|
||||
(
|
||||
tag = AssignOperationLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getLeftOperand().getResultType()
|
||||
tag = AssignOperationConvertLeftTag() and
|
||||
resultType = getTypeForPRValue(getConvertedLeftOperandType())
|
||||
or
|
||||
tag = AssignOperationOpTag() and
|
||||
opcode = getOpcode() and
|
||||
resultType = getConvertedLeftOperandType()
|
||||
or
|
||||
tag = AssignmentStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getResultType()
|
||||
or
|
||||
leftOperandNeedsConversion() and
|
||||
opcode instanceof Opcode::Convert and
|
||||
(
|
||||
tag = AssignOperationConvertLeftTag() and
|
||||
resultType = getConvertedLeftOperandType()
|
||||
or
|
||||
tag = AssignOperationConvertResultTag() and
|
||||
resultType = getLeftOperand().getResultType()
|
||||
)
|
||||
tag = AssignOperationConvertResultTag() and
|
||||
resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1484,7 +1436,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
|
||||
opcode = getOpcode() and
|
||||
(opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub)
|
||||
) and
|
||||
result = max(getResultType().(PointerType).getBaseType().getSize())
|
||||
result = getElementSize(expr.getType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -1566,13 +1518,10 @@ class TranslatedConstantAllocationSize extends TranslatedAllocationSize {
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(AllocationSizeTag()) }
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = AllocationSizeTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = expr.getAllocator().getParameter(0).getUnspecifiedType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -1605,11 +1554,8 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getExtent().getFirstInstruction() }
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
isGLValue = false and
|
||||
resultType = expr.getAllocator().getParameter(0).getUnspecifiedType() and
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType()) and
|
||||
(
|
||||
// Convert the extent to `size_t`, because the AST doesn't do this already.
|
||||
tag = AllocationExtentConvertTag() and opcode instanceof Opcode::Convert
|
||||
@@ -1681,7 +1627,7 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
|
||||
tag = CallTargetTag() and result = expr.getAllocator()
|
||||
}
|
||||
|
||||
final override Type getCallResultType() { result = expr.getAllocator().getUnspecifiedType() }
|
||||
final override Type getCallResultType() { result = expr.getAllocator().getType() }
|
||||
|
||||
final override TranslatedExpr getQualifier() { none() }
|
||||
|
||||
@@ -1736,13 +1682,10 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = getDestructorCall() }
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::FieldAddress and
|
||||
resultType = expr.getTarget().getUnspecifiedType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(expr.getTarget().getType())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -1789,9 +1732,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
|
||||
|
||||
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
not resultIsVoid() and
|
||||
(
|
||||
(
|
||||
@@ -1802,8 +1743,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
|
||||
tag = ConditionValueResultTempAddressTag()
|
||||
) and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
(
|
||||
if expr.isGLValueCategory()
|
||||
then resultType = getTypeForGLValue(any(UnknownType t)) // glvalue to a glvalue
|
||||
else resultType = getTypeForGLValue(expr.getType()) // glvalue to the result type
|
||||
)
|
||||
or
|
||||
(
|
||||
not thenIsVoid() and tag = ConditionValueTrueStoreTag()
|
||||
@@ -1811,13 +1755,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
|
||||
not elseIsVoid() and tag = ConditionValueFalseStoreTag()
|
||||
) and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getResultType()
|
||||
or
|
||||
tag = ConditionValueResultLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = getResultType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1885,7 +1827,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
not resultIsVoid() and
|
||||
tag = ConditionValueTempVar() and
|
||||
type = getResultType()
|
||||
@@ -1945,7 +1887,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
|
||||
}
|
||||
|
||||
private predicate thenIsVoid() {
|
||||
getThen().getResultType() instanceof VoidType
|
||||
getThen().getResultType().getIRType() instanceof IRVoidType
|
||||
or
|
||||
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
|
||||
// thrown, rather than `void`. Handle that case here.
|
||||
@@ -1953,14 +1895,14 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
|
||||
}
|
||||
|
||||
private predicate elseIsVoid() {
|
||||
getElse().getResultType() instanceof VoidType
|
||||
getElse().getResultType().getIRType() instanceof IRVoidType
|
||||
or
|
||||
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
|
||||
// thrown, rather than `void`. Handle that case here.
|
||||
expr.getElse() instanceof ThrowExpr
|
||||
}
|
||||
|
||||
private predicate resultIsVoid() { getResultType() instanceof VoidType }
|
||||
private predicate resultIsVoid() { getResultType().getIRType() instanceof IRVoidType }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1969,13 +1911,10 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
|
||||
abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr {
|
||||
override ThrowExpr expr;
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = ThrowTag() and
|
||||
opcode = getThrowOpcode() and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -2002,15 +1941,12 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType, isGLValue)
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType)
|
||||
or
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getExceptionType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getExceptionType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -2031,9 +1967,9 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
|
||||
result = getIRTempVariable(expr, ThrowTempVar())
|
||||
}
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
tag = ThrowTempVar() and
|
||||
type = getExceptionType()
|
||||
type = getTypeForPRValue(getExceptionType())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -2047,10 +1983,10 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
|
||||
)
|
||||
}
|
||||
|
||||
final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag = ThrowTag() and
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getExceptionType()
|
||||
result = getTypeForPRValue(getExceptionType())
|
||||
}
|
||||
|
||||
override Instruction getTargetAddress() {
|
||||
@@ -2065,7 +2001,7 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
|
||||
|
||||
final override Opcode getThrowOpcode() { result instanceof Opcode::ThrowValue }
|
||||
|
||||
private Type getExceptionType() { result = expr.getUnspecifiedType() }
|
||||
private Type getExceptionType() { result = expr.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2123,13 +2059,10 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode = getOpcode() and
|
||||
resultType = getResultType() and
|
||||
isGLValue = isResultGLValue()
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -2196,13 +2129,10 @@ abstract class TranslatedNewOrNewArrayExpr extends TranslatedNonConstantExpr, In
|
||||
id = 1 and result = getInitialization()
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::Convert and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
@@ -2363,9 +2293,7 @@ class TranslatedConditionDeclExpr extends TranslatedNonConstantExpr {
|
||||
child = getConditionExpr() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -2414,23 +2342,18 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont
|
||||
result = getInstruction(LoadTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(expr.getType())
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Uninitialized and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -2456,16 +2379,16 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont
|
||||
result = getTempVariable(LambdaTempVar())
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
tag = LambdaTempVar() and
|
||||
type = getResultType()
|
||||
type = getTypeForPRValue(expr.getType())
|
||||
}
|
||||
|
||||
final override Instruction getTargetAddress() {
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
final override Type getTargetType() { result = getResultType() }
|
||||
final override Type getTargetType() { result = expr.getType() }
|
||||
|
||||
private predicate hasInitializer() { exists(getInitialization()) }
|
||||
|
||||
@@ -2496,13 +2419,10 @@ class TranslatedStmtExpr extends TranslatedNonConstantExpr {
|
||||
result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
opcode instanceof Opcode::CopyValue and
|
||||
tag instanceof OnlyInstructionTag and
|
||||
resultType = expr.getType() and
|
||||
isGLValue = false
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
|
||||
@@ -2589,3 +2509,26 @@ private predicate exprImmediatelyDiscarded(Expr expr) {
|
||||
or
|
||||
exists(ForStmt for | for.getUpdate() = expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of an `__assume` expression. We currently translate these as `NoOp`. In the
|
||||
* future, we will probably want to do something better. At a minimum, we can model `__assume(0)` as
|
||||
* `Unreached`.
|
||||
*/
|
||||
class TranslatedAssumeExpr extends TranslatedSingleInstructionExpr {
|
||||
override AssumeExpr expr;
|
||||
|
||||
final override Opcode getOpcode() { result instanceof Opcode::NoOp }
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.IRUtilities
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
@@ -95,6 +96,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
result = getInstruction(UnmodeledUseTag())
|
||||
or
|
||||
tag = UnmodeledUseTag() and
|
||||
result = getInstruction(AliasedUseTag())
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
result = getInstruction(ExitFunctionTag())
|
||||
)
|
||||
}
|
||||
@@ -115,55 +119,46 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
or
|
||||
(
|
||||
child = getDestructorDestructionList() and
|
||||
if getReturnType() instanceof VoidType
|
||||
then result = getInstruction(ReturnTag())
|
||||
else result = getInstruction(ReturnValueAddressTag())
|
||||
if hasReturnValue()
|
||||
then result = getInstruction(ReturnValueAddressTag())
|
||||
else result = getInstruction(ReturnTag())
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
(
|
||||
tag = EnterFunctionTag() and
|
||||
opcode instanceof Opcode::EnterFunction and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = UnmodeledDefinitionTag() and
|
||||
opcode instanceof Opcode::UnmodeledDefinition and
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
resultType = getUnknownType()
|
||||
or
|
||||
tag = AliasedDefinitionTag() and
|
||||
opcode instanceof Opcode::AliasedDefinition and
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
resultType = getUnknownType()
|
||||
or
|
||||
tag = InitializeThisTag() and
|
||||
opcode instanceof Opcode::InitializeThis and
|
||||
resultType = getThisType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getThisType())
|
||||
or
|
||||
tag = ReturnValueAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getReturnType() and
|
||||
not resultType instanceof VoidType and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getReturnType()) and
|
||||
hasReturnValue()
|
||||
or
|
||||
(
|
||||
tag = ReturnTag() and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false and
|
||||
if getReturnType() instanceof VoidType
|
||||
then opcode instanceof Opcode::ReturnVoid
|
||||
else opcode instanceof Opcode::ReturnValue
|
||||
resultType = getVoidType() and
|
||||
if hasReturnValue()
|
||||
then opcode instanceof Opcode::ReturnValue
|
||||
else opcode instanceof Opcode::ReturnVoid
|
||||
)
|
||||
or
|
||||
tag = UnwindTag() and
|
||||
opcode instanceof Opcode::Unwind and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false and
|
||||
resultType = getVoidType() and
|
||||
(
|
||||
// Only generate the `Unwind` instruction if there is any exception
|
||||
// handling present in the function.
|
||||
@@ -173,13 +168,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
or
|
||||
tag = UnmodeledUseTag() and
|
||||
opcode instanceof Opcode::UnmodeledUse and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
opcode instanceof Opcode::AliasedUse and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = ExitFunctionTag() and
|
||||
opcode instanceof Opcode::ExitFunction and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -197,8 +194,12 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
operandTag instanceof UnmodeledUseOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnmodeledDefinitionInstruction()
|
||||
or
|
||||
tag = ReturnTag() and
|
||||
not getReturnType() instanceof VoidType and
|
||||
hasReturnValue() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ReturnValueAddressTag())
|
||||
@@ -208,11 +209,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
)
|
||||
}
|
||||
|
||||
final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag = ReturnTag() and
|
||||
not getReturnType() instanceof VoidType and
|
||||
hasReturnValue() and
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getReturnType()
|
||||
result = getTypeForPRValue(getReturnType())
|
||||
or
|
||||
tag = AliasedUseTag() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnknownType()
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
@@ -220,10 +225,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
result = getReturnVariable()
|
||||
}
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
tag = ReturnValueTempVar() and
|
||||
type = getReturnType() and
|
||||
not type instanceof VoidType
|
||||
hasReturnValue() and
|
||||
type = getTypeForPRValue(getReturnType())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,6 +246,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
result = getIRTempVariable(func, ReturnValueTempVar())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function has a non-`void` return type.
|
||||
*/
|
||||
final predicate hasReturnValue() { not func.getUnspecifiedType() instanceof VoidType }
|
||||
|
||||
/**
|
||||
* Gets the single `UnmodeledDefinition` instruction for this function.
|
||||
*/
|
||||
@@ -271,7 +281,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
* parameters and local variables, plus any global variables or static data members that are
|
||||
* directly accessed by the function.
|
||||
*/
|
||||
final predicate hasUserVariable(Variable var, Type type) {
|
||||
final predicate hasUserVariable(Variable var, CppType type) {
|
||||
(
|
||||
(
|
||||
var instanceof GlobalOrNamespaceVariable
|
||||
@@ -287,10 +297,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
or
|
||||
var.(Parameter).getCatchBlock().getEnclosingFunction() = func
|
||||
) and
|
||||
type = getVariableType(var)
|
||||
type = getTypeForPRValue(getVariableType(var))
|
||||
}
|
||||
|
||||
final private Type getReturnType() { result = func.getUnspecifiedType() }
|
||||
final Type getReturnType() { result = func.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,18 +345,14 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getVariableType(param) and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getVariableType(param))
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::InitializeParameter and
|
||||
resultType = getVariableType(param) and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(getVariableType(param))
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
@@ -404,9 +410,7 @@ class TranslatedConstructorInitList extends TranslatedElement, InitializationCon
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -469,9 +473,7 @@ class TranslatedDestructorDestructionList extends TranslatedElement,
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
@@ -79,9 +80,7 @@ abstract class TranslatedListInitialization extends TranslatedInitialization, In
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -150,13 +149,10 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio
|
||||
not expr instanceof StringLiteral
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getContext().getTargetType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(getContext().getTargetType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -188,20 +184,16 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio
|
||||
class TranslatedStringLiteralInitialization extends TranslatedDirectInitialization {
|
||||
override StringLiteral expr;
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
// Load the string literal to make it a prvalue of type `char[len]`
|
||||
tag = InitializerLoadStringTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getInitializer().getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
// Store the string into the target.
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getInitializer().getResultType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
exists(int startIndex, int elementCount |
|
||||
// If the initializer string isn't large enough to fill the target, then
|
||||
@@ -213,26 +205,22 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
// space in the target array.
|
||||
tag = ZeroPadStringConstantTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
|
||||
or
|
||||
// The index of the first element to be zero initialized.
|
||||
tag = ZeroPadStringElementIndexTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getIntType() and
|
||||
isGLValue = false
|
||||
resultType = getIntType()
|
||||
or
|
||||
// Compute the address of the first element to be zero initialized.
|
||||
tag = ZeroPadStringElementAddressTag() and
|
||||
opcode instanceof Opcode::PointerAdd and
|
||||
resultType = getElementType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getElementType())
|
||||
or
|
||||
// Store the constant zero into the remainder of the string.
|
||||
tag = ZeroPadStringStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -326,6 +314,13 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
)
|
||||
}
|
||||
|
||||
override predicate needsUnknownOpaqueType(int byteSize) {
|
||||
exists(int elementCount |
|
||||
zeroInitRange(_, elementCount) and
|
||||
byteSize = elementCount * getElementType().getSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getInstructionResultSize(InstructionTag tag) {
|
||||
exists(int elementCount |
|
||||
zeroInitRange(_, elementCount) and
|
||||
@@ -338,7 +333,7 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
}
|
||||
|
||||
private Type getElementType() {
|
||||
result = getContext().getTargetType().(ArrayType).getBaseType().getUnspecifiedType()
|
||||
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +343,7 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
private predicate zeroInitRange(int startIndex, int elementCount) {
|
||||
exists(int targetCount |
|
||||
startIndex = expr.getUnspecifiedType().(ArrayType).getArraySize() and
|
||||
targetCount = getContext().getTargetType().(ArrayType).getArraySize() and
|
||||
targetCount = getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and
|
||||
elementCount = targetCount - startIndex and
|
||||
elementCount > 0
|
||||
)
|
||||
@@ -359,9 +354,7 @@ class TranslatedConstructorInitialization extends TranslatedDirectInitialization
|
||||
StructorCallContext {
|
||||
override ConstructorCall expr;
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -412,13 +405,10 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
|
||||
*/
|
||||
final int getOrder() { result = field.getInitializationOrder() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = getFieldAddressTag() and
|
||||
opcode instanceof Opcode::FieldAddress and
|
||||
resultType = field.getUnspecifiedType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(field.getType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -481,20 +471,16 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
|
||||
TTranslatedFieldValueInitialization {
|
||||
TranslatedFieldValueInitialization() { this = TTranslatedFieldValueInitialization(ast, field) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue)
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType)
|
||||
or
|
||||
tag = getFieldDefaultValueTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = field.getUnspecifiedType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(field.getType())
|
||||
or
|
||||
tag = getFieldDefaultValueStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = field.getUnspecifiedType() and
|
||||
isGLValue = false
|
||||
resultType = getTypeForPRValue(field.getUnspecifiedType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -557,18 +543,14 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(getElementIndexTag()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = getElementIndexTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getIntType() and
|
||||
isGLValue = false
|
||||
resultType = getIntType()
|
||||
or
|
||||
tag = getElementAddressTag() and
|
||||
opcode instanceof Opcode::PointerAdd and
|
||||
resultType = getElementType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getElementType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -606,7 +588,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
|
||||
final ArrayOrVectorAggregateLiteral getInitList() { result = initList }
|
||||
|
||||
final Type getElementType() { result = initList.getElementType().getUnspecifiedType() }
|
||||
final Type getElementType() { result = initList.getElementType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -659,20 +641,16 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
this = TTranslatedElementValueInitialization(initList, elementIndex, elementCount)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue)
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType)
|
||||
or
|
||||
tag = getElementDefaultValueTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
resultType = getDefaultValueType() and
|
||||
isGLValue = false
|
||||
resultType = getDefaultValueType()
|
||||
or
|
||||
tag = getElementDefaultValueStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getDefaultValueType() and
|
||||
isGLValue = false
|
||||
resultType = getDefaultValueType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -726,6 +704,10 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
|
||||
override int getElementIndex() { result = elementIndex }
|
||||
|
||||
override predicate needsUnknownOpaqueType(int byteSize) {
|
||||
elementCount != 0 and byteSize = elementCount * getElementType().getSize()
|
||||
}
|
||||
|
||||
private InstructionTag getElementDefaultValueTag() {
|
||||
result = InitializerElementDefaultValueTag()
|
||||
}
|
||||
@@ -734,8 +716,10 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
result = InitializerElementDefaultValueStoreTag()
|
||||
}
|
||||
|
||||
private Type getDefaultValueType() {
|
||||
if elementCount = 1 then result = getElementType() else result instanceof UnknownType
|
||||
private CppType getDefaultValueType() {
|
||||
if elementCount = 1
|
||||
then result = getTypeForPRValue(getElementType())
|
||||
else result = getUnknownOpaqueType(elementCount * getElementType().getSize())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -766,13 +750,10 @@ abstract class TranslatedStructorCallFromStructor extends TranslatedElement, Str
|
||||
abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStructor {
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::ConvertToBase and
|
||||
resultType = call.getTarget().getDeclaringType().getUnspecifiedType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(call.getTarget().getDeclaringType())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -822,9 +803,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
|
||||
result = getStructorCall().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.internal.IRUtilities
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
@@ -35,13 +36,10 @@ class TranslatedEmptyStmt extends TranslatedStmt {
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::NoOp and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -63,9 +61,7 @@ class TranslatedDeclStmt extends TranslatedStmt {
|
||||
|
||||
override TranslatedElement getChild(int id) { result = getDeclarationEntry(id) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -112,9 +108,7 @@ class TranslatedExprStmt extends TranslatedStmt {
|
||||
|
||||
override TranslatedElement getChild(int id) { id = 0 and result = getExpr() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -145,13 +139,10 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, InitializationCont
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getEnclosingFunction().getReturnVariable().getType() and
|
||||
isGLValue = true
|
||||
resultType = getTypeForGLValue(getEnclosingFunction().getReturnType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -174,7 +165,7 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, InitializationCont
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override Type getTargetType() { result = getEnclosingFunction().getReturnVariable().getType() }
|
||||
override Type getTargetType() { result = getEnclosingFunction().getReturnType() }
|
||||
|
||||
TranslatedInitialization getInitialization() {
|
||||
result = getTranslatedInitialization(stmt.getExpr().getFullyConverted())
|
||||
@@ -188,13 +179,10 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::NoOp and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -218,9 +206,7 @@ class TranslatedTryStmt extends TranslatedStmt {
|
||||
result = getHandler(id - 1)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -262,14 +248,11 @@ class TranslatedBlock extends TranslatedStmt {
|
||||
|
||||
override TranslatedElement getChild(int id) { result = getStmt(id) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
isEmpty() and
|
||||
opcode instanceof Opcode::NoOp and
|
||||
tag = OnlyInstructionTag() and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
@@ -330,13 +313,10 @@ abstract class TranslatedHandler extends TranslatedStmt {
|
||||
class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
||||
TranslatedCatchByTypeHandler() { exists(stmt.getParameter()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = CatchTag() and
|
||||
opcode instanceof Opcode::CatchByType and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
@@ -362,9 +342,9 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
||||
)
|
||||
}
|
||||
|
||||
override Type getInstructionExceptionType(InstructionTag tag) {
|
||||
override CppType getInstructionExceptionType(InstructionTag tag) {
|
||||
tag = CatchTag() and
|
||||
result = stmt.getParameter().getType()
|
||||
result = getTypeForPRValue(stmt.getParameter().getType())
|
||||
}
|
||||
|
||||
private TranslatedParameter getParameter() {
|
||||
@@ -378,13 +358,10 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
||||
class TranslatedCatchAnyHandler extends TranslatedHandler {
|
||||
TranslatedCatchAnyHandler() { not exists(stmt.getParameter()) }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = CatchTag() and
|
||||
opcode instanceof Opcode::CatchAny and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -436,9 +413,7 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
@@ -466,9 +441,7 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
|
||||
id = 1 and result = getBody()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -597,9 +570,7 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
result = getCondition().getFirstInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -649,13 +620,10 @@ class TranslatedJumpStmt extends TranslatedStmt {
|
||||
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::NoOp and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
@@ -693,13 +661,10 @@ class TranslatedSwitchStmt extends TranslatedStmt {
|
||||
id = 1 and result = getBody()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = SwitchBranchTag() and
|
||||
opcode instanceof Opcode::Switch and
|
||||
resultType instanceof VoidType and
|
||||
isGLValue = false
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -727,37 +692,20 @@ class TranslatedSwitchStmt extends TranslatedStmt {
|
||||
class TranslatedAsmStmt extends TranslatedStmt {
|
||||
override AsmStmt stmt;
|
||||
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
override TranslatedExpr getChild(int id) {
|
||||
result = getTranslatedExpr(stmt.getChild(id).(Expr).getFullyConverted())
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
if exists(stmt.getChild(0))
|
||||
then result = getInstruction(AsmInputTag(0))
|
||||
if exists(getChild(0))
|
||||
then result = getChild(0).getFirstInstruction()
|
||||
else result = getInstruction(AsmTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(
|
||||
Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
|
||||
) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = AsmTag() and
|
||||
opcode instanceof Opcode::InlineAsm and
|
||||
resultType instanceof UnknownType and
|
||||
isGLValue = false
|
||||
or
|
||||
exists(int index, VariableAccess va |
|
||||
tag = AsmInputTag(index) and
|
||||
stmt.getChild(index) = va and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = va.getType().getUnspecifiedType() and
|
||||
isGLValue = true
|
||||
)
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
exists(int index |
|
||||
tag = AsmInputTag(index) and
|
||||
result = getIRUserVariable(stmt.getEnclosingFunction(),
|
||||
stmt.getChild(index).(VariableAccess).getTarget())
|
||||
)
|
||||
resultType = getUnknownType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -768,29 +716,28 @@ class TranslatedAsmStmt extends TranslatedStmt {
|
||||
exists(int index |
|
||||
tag = AsmTag() and
|
||||
operandTag = asmOperand(index) and
|
||||
result = getInstruction(AsmInputTag(index))
|
||||
result = getChild(index).getResult()
|
||||
)
|
||||
}
|
||||
|
||||
final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag = AsmTag() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result instanceof UnknownType
|
||||
result = getUnknownType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = AsmTag() and
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
exists(int index |
|
||||
tag = AsmInputTag(index) and
|
||||
kind instanceof GotoEdge and
|
||||
if exists(stmt.getChild(index + 1))
|
||||
then result = getInstruction(AsmInputTag(index + 1))
|
||||
child = getChild(index) and
|
||||
if exists(getChild(index + 1))
|
||||
then result = getChild(index + 1).getFirstInstruction()
|
||||
else result = getInstruction(AsmTag())
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.IRImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
import IRTypeSanity
|
||||
|
||||
@@ -5,6 +5,7 @@ import Imports::TempVariableTag
|
||||
private import Imports::IRUtilities
|
||||
private import Imports::TTempVariableTag
|
||||
private import Imports::TIRVariable
|
||||
private import Imports::IRType
|
||||
|
||||
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
|
||||
result.getVariable() = var and
|
||||
@@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::Type getType();
|
||||
final Language::Type getType() { getLanguageType().hasType(result, false) }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the variable.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
abstract Language::LanguageType getLanguageType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
@@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable {
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRUserVariable() { this = TIRUserVariable(var, type, func) }
|
||||
|
||||
@@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
result = getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
/**
|
||||
* Gets the original user-declared variable.
|
||||
@@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
Language::AST ast;
|
||||
TempVariableTag tag;
|
||||
Language::Type type;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
|
||||
final override Language::Type getType() { result = type }
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAST() { result = ast }
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRVariable
|
||||
import Operand
|
||||
private import internal.InstructionImports as Imports
|
||||
import Imports::EdgeKind
|
||||
import Imports::IRType
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
@@ -49,7 +50,8 @@ module InstructionSanity {
|
||||
(
|
||||
opcode instanceof ReadSideEffectOpcode or
|
||||
opcode instanceof Opcode::InlineAsm or
|
||||
opcode instanceof Opcode::CallSideEffect
|
||||
opcode instanceof Opcode::CallSideEffect or
|
||||
opcode instanceof Opcode::AliasedUse
|
||||
) and
|
||||
tag instanceof SideEffectOperandTag
|
||||
)
|
||||
@@ -113,10 +115,12 @@ module InstructionSanity {
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func |
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
func = operand.getUse().getEnclosingFunction() and
|
||||
message = "Operand missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString()
|
||||
+ "' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -260,6 +264,7 @@ module InstructionSanity {
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not useOperand.getUse() instanceof UnmodeledUseInstruction and
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
if getResultType() instanceof Language::VoidType
|
||||
if getResultIRType() instanceof IRVoidType
|
||||
then result = "v"
|
||||
else
|
||||
if hasMemoryResult()
|
||||
@@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[type]
|
||||
private string getValueCategoryString(string type) {
|
||||
if isGLValue() then result = "glval<" + type + ">" else result = type
|
||||
}
|
||||
|
||||
string getResultTypeString() {
|
||||
exists(string valcat |
|
||||
valcat = getValueCategoryString(getResultType().toString()) and
|
||||
if
|
||||
getResultType() instanceof Language::UnknownType and
|
||||
not isGLValue() and
|
||||
exists(getResultSize())
|
||||
then result = valcat + "[" + getResultSize().toString() + "]"
|
||||
else result = valcat
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a human-readable string that uniquely identifies this instruction
|
||||
* within the function. This string is used to refer to this instruction when
|
||||
@@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction {
|
||||
*
|
||||
* Example: `r1_1(int*)`
|
||||
*/
|
||||
final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" }
|
||||
final string getResultString() {
|
||||
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operands of this instruction, suitable for
|
||||
@@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction {
|
||||
result = Construction::getInstructionUnconvertedResultExpression(this)
|
||||
}
|
||||
|
||||
final Language::LanguageType getResultLanguageType() {
|
||||
result = Construction::getInstructionResultType(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `isGLValue()` holds, then the result type of this instruction should be
|
||||
* thought of as "pointer to `getResultType()`".
|
||||
*/
|
||||
final Language::Type getResultType() { Construction::instructionHasType(this, result, _) }
|
||||
final Language::Type getResultType() {
|
||||
exists(Language::LanguageType resultType |
|
||||
resultType = getResultLanguageType() and
|
||||
(
|
||||
resultType.hasUnspecifiedType(result, _)
|
||||
or
|
||||
not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the result produced by this instruction is a glvalue. If this
|
||||
@@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* result of the `Load` instruction is a prvalue of type `int`, representing
|
||||
* the integer value loaded from variable `x`.
|
||||
*/
|
||||
final predicate isGLValue() { Construction::instructionHasType(this, _, true) }
|
||||
final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the result produced by this instruction, in bytes. If the
|
||||
@@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* If `this.isGLValue()` holds for this instruction, the value of
|
||||
* `getResultSize()` will always be the size of a pointer.
|
||||
*/
|
||||
final int getResultSize() {
|
||||
if isGLValue()
|
||||
then
|
||||
// a glvalue is always pointer-sized.
|
||||
result = Language::getPointerSize()
|
||||
else
|
||||
if getResultType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionResultSize(this)
|
||||
else result = Language::getTypeSize(getResultType())
|
||||
}
|
||||
final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the opcode that specifies the operation performed by this instruction.
|
||||
@@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction {
|
||||
* An instruction that catches an exception of a specific type.
|
||||
*/
|
||||
class CatchByTypeInstruction extends CatchInstruction {
|
||||
Language::Type exceptionType;
|
||||
Language::LanguageType exceptionType;
|
||||
|
||||
CatchByTypeInstruction() {
|
||||
getOpcode() instanceof Opcode::CatchByType and
|
||||
@@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction {
|
||||
/**
|
||||
* Gets the type of exception to be caught.
|
||||
*/
|
||||
final Language::Type getExceptionType() { result = exceptionType }
|
||||
final Language::LanguageType getExceptionType() { result = exceptionType }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction {
|
||||
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that consumes all escaped memory on exit from the function.
|
||||
*/
|
||||
class AliasedUseInstruction extends Instruction {
|
||||
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
|
||||
}
|
||||
|
||||
class UnmodeledUseInstruction extends Instruction {
|
||||
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
private import internal.IRInternal
|
||||
import Instruction
|
||||
import IRBlock
|
||||
private import Instruction
|
||||
private import IRBlock
|
||||
private import internal.OperandImports as Imports
|
||||
import Imports::MemoryAccessKind
|
||||
import Imports::Overlap
|
||||
private import Imports::MemoryAccessKind
|
||||
private import Imports::IRType
|
||||
private import Imports::Overlap
|
||||
private import Imports::OperandTag
|
||||
|
||||
cached
|
||||
@@ -143,22 +144,40 @@ class Operand extends TOperand {
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
Language::Type getType() { result = getAnyDef().getResultType() }
|
||||
Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
|
||||
|
||||
/**
|
||||
* Gets the language-neutral type of the value consumed by this operand. This is usually the same
|
||||
* as the result type of the definition instruction consumed by this operand. For register
|
||||
* operands, this is always the case. For some memory operands, the operand type may be different
|
||||
* from the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final IRType getIRType() { result = getLanguageType().getIRType() }
|
||||
|
||||
/**
|
||||
* Gets the type of the value consumed by this operand. This is usually the same as the
|
||||
* result type of the definition instruction consumed by this operand. For register operands,
|
||||
* this is always the case. For some memory operands, the operand type may be different from
|
||||
* the definition type, such as in the case of a partial read or a read from a pointer that
|
||||
* has been cast to a different type.
|
||||
*/
|
||||
final Language::Type getType() { getLanguageType().hasType(result, _) }
|
||||
|
||||
/**
|
||||
* Holds if the value consumed by this operand is a glvalue. If this
|
||||
* holds, the value of the operand represents the address of a location,
|
||||
* and the type of the location is given by `getType()`. If this does
|
||||
* not hold, the value of the operand represents a value whose type is
|
||||
* given by `getResultType()`.
|
||||
* given by `getType()`.
|
||||
*/
|
||||
predicate isGLValue() { getAnyDef().isGLValue() }
|
||||
final predicate isGLValue() { getLanguageType().hasType(_, true) }
|
||||
|
||||
/**
|
||||
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
|
||||
* a known constant size, this predicate does not hold.
|
||||
*/
|
||||
int getSize() { result = Language::getTypeSize(getType()) }
|
||||
final int getSize() { result = getLanguageType().getByteSize() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,11 +189,6 @@ class MemoryOperand extends Operand {
|
||||
this = TPhiOperand(_, _, _, _)
|
||||
}
|
||||
|
||||
override predicate isGLValue() {
|
||||
// A `MemoryOperand` can never be a glvalue
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of memory access performed by the operand.
|
||||
*/
|
||||
@@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
|
||||
class TypedOperand extends NonPhiMemoryOperand {
|
||||
override TypedOperandTag tag;
|
||||
|
||||
final override Language::Type getType() {
|
||||
final override Language::LanguageType getLanguageType() {
|
||||
result = Construction::getInstructionOperandType(useInstr, tag)
|
||||
}
|
||||
}
|
||||
@@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
final override int getSize() {
|
||||
if getType() instanceof Language::UnknownType
|
||||
then result = Construction::getInstructionOperandSize(useInstr, tag)
|
||||
else result = Language::getTypeSize(getType())
|
||||
}
|
||||
|
||||
override MemoryAccessKind getMemoryAccess() {
|
||||
useInstr instanceof AliasedUseInstruction and
|
||||
result instanceof NonLocalMayMemoryAccess
|
||||
or
|
||||
useInstr instanceof CallSideEffectInstruction and
|
||||
result instanceof EscapedMayMemoryAccess
|
||||
or
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
private import cpp
|
||||
private import internal.ValueNumberingImports
|
||||
private import IR
|
||||
|
||||
/**
|
||||
@@ -23,31 +23,32 @@ newtype TValueNumber =
|
||||
initializeParameterValueNumber(_, irFunc, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
|
||||
TConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
constantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
ValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
@@ -59,7 +60,7 @@ newtype TValueNumber =
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() { result = getExampleInstruction().getResultId() }
|
||||
|
||||
final Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
@@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate stringConstantValueNumber(
|
||||
StringConstantInstruction instr, IRFunction irFunc, Type type, string value
|
||||
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue().getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress
|
||||
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
@@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber(
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
|
||||
int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeft()) = leftOperand and
|
||||
valueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getResultIRType() = type and
|
||||
valueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand
|
||||
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
|
||||
Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
@@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber(
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultType() instanceof VoidType and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
@@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Type type, string value |
|
||||
exists(IRType type, string value |
|
||||
stringConstantValueNumber(instr, irFunc, type, value) and
|
||||
result = TStringConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
exists(Language::Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
exists(Opcode opcode, IRType type, ValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
exists(
|
||||
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
||||
|
|
||||
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
|
||||
Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand
|
||||
|
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag
|
||||
import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities
|
||||
import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
|
||||
@@ -4,34 +4,52 @@ private import Alias
|
||||
private import SSAConstruction
|
||||
private import DebugSSA
|
||||
|
||||
bindingset[offset]
|
||||
private string getKeySuffixForOffset(int offset) {
|
||||
if offset % 2 = 0 then result = "" else result = "_Chi"
|
||||
}
|
||||
|
||||
bindingset[offset]
|
||||
private int getIndexForOffset(int offset) { result = offset / 2 }
|
||||
|
||||
/**
|
||||
* Property provide that dumps the memory access of each result. Useful for debugging SSA
|
||||
* construction.
|
||||
*/
|
||||
class PropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
exists(MemoryLocation location |
|
||||
location = getResultMemoryLocation(instruction) and
|
||||
(
|
||||
key = "ResultMemoryLocation" and result = location.toString()
|
||||
or
|
||||
key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString()
|
||||
key = "ResultMemoryLocation" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getResultMemoryLocation(instruction)
|
||||
|
|
||||
loc.toString(), ","
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation location |
|
||||
location = getOperandMemoryLocation(instruction.getAnOperand()) and
|
||||
(
|
||||
key = "OperandMemoryAccess" and result = location.toString()
|
||||
or
|
||||
key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString()
|
||||
key = "ResultVirtualVariable" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getResultMemoryLocation(instruction)
|
||||
|
|
||||
loc.getVirtualVariable().toString(), ","
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
|
||||
defBlock.getInstruction(defIndex) = instruction and
|
||||
key = "DefinitionRank[" + useLocation.toString() + "]" and
|
||||
key = "OperandMemoryLocation" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getOperandMemoryLocation(instruction.getAnOperand())
|
||||
|
|
||||
loc.toString(), ","
|
||||
)
|
||||
or
|
||||
key = "OperandVirtualVariable" and
|
||||
result = strictconcat(MemoryLocation loc |
|
||||
loc = getOperandMemoryLocation(instruction.getAnOperand())
|
||||
|
|
||||
loc.getVirtualVariable().toString(), ","
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
|
||||
defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
|
||||
key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and
|
||||
result = defRank.toString()
|
||||
)
|
||||
or
|
||||
@@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider {
|
||||
result = useRank.toString()
|
||||
)
|
||||
or
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
|
||||
defBlock.getInstruction(defIndex) = instruction and
|
||||
key = "DefinitionReachesUse[" + useLocation.toString() + "]" and
|
||||
exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
|
||||
hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
|
||||
defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
|
||||
key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString()
|
||||
+ "]" and
|
||||
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
|
||||
exists(Instruction useInstruction |
|
||||
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import SSAConstructionInternal
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import SSAConstructionImports
|
||||
private import NewIR
|
||||
|
||||
private class OldBlock = Reachability::ReachableBlock;
|
||||
@@ -18,7 +15,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate functionHasIR(Function func) {
|
||||
predicate functionHasIR(Language::Function func) {
|
||||
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
|
||||
}
|
||||
|
||||
@@ -42,7 +39,7 @@ private module Cached {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction and
|
||||
hasChiNode(_, oldInstruction)
|
||||
} or
|
||||
Unreached(Function function) {
|
||||
Unreached(Language::Function function) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
function = oldInstruction.getEnclosingFunction() and
|
||||
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
|
||||
@@ -50,12 +47,14 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) {
|
||||
predicate hasTempVariable(
|
||||
Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
|
||||
) {
|
||||
exists(OldIR::IRTempVariable var |
|
||||
var.getEnclosingFunction() = func and
|
||||
var.getAST() = ast and
|
||||
var.getTag() = tag and
|
||||
var.getType() = type
|
||||
var.getLanguageType() = type
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,24 +134,12 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
|
||||
Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instr) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
result = oldOperand.getType()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand |
|
||||
oldInstruction = getOldInstruction(instr) and
|
||||
oldOperand = oldInstruction.getAnOperand() and
|
||||
tag = oldOperand.getOperandTag() and
|
||||
// Only return a result for operands that need an explicit result size.
|
||||
oldOperand.getType() instanceof UnknownType and
|
||||
result = oldOperand.getSize()
|
||||
result = oldOperand.getLanguageType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -196,20 +183,21 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getConvertedResultExpression()
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
|
||||
* that node is its successor in the new successor relation, and the Chi node's successors are
|
||||
* the new instructions generated from the successors of the old instruction
|
||||
*/
|
||||
|
||||
cached
|
||||
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
if hasChiNode(_, getOldInstruction(instruction))
|
||||
@@ -252,7 +240,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Locatable getInstructionAST(Instruction instruction) {
|
||||
Language::AST getInstructionAST(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction)
|
||||
or
|
||||
@@ -270,29 +258,25 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) {
|
||||
Language::LanguageType getInstructionResultType(Instruction instruction) {
|
||||
exists(OldInstruction oldInstruction |
|
||||
instruction = WrappedInstruction(oldInstruction) and
|
||||
type = oldInstruction.getResultType() and
|
||||
if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false
|
||||
result = oldInstruction.getResultLanguageType()
|
||||
)
|
||||
or
|
||||
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
|
||||
instruction = Chi(oldInstruction) and
|
||||
hasChiNode(vvar, oldInstruction) and
|
||||
type = vvar.getType() and
|
||||
isGLValue = false
|
||||
result = vvar.getType()
|
||||
)
|
||||
or
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
type = location.getType() and
|
||||
isGLValue = false
|
||||
result = location.getType()
|
||||
)
|
||||
or
|
||||
instruction = Unreached(_) and
|
||||
type instanceof VoidType and
|
||||
isGLValue = false
|
||||
result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -338,7 +322,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Field getInstructionField(Instruction instruction) {
|
||||
Language::Field getInstructionField(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
|
||||
}
|
||||
|
||||
@@ -348,7 +332,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Function getInstructionFunction(Instruction instruction) {
|
||||
Language::Function getInstructionFunction(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
|
||||
}
|
||||
|
||||
@@ -358,19 +342,19 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
Language::StringLiteral getInstructionStringLiteral(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
|
||||
}
|
||||
|
||||
cached
|
||||
BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
|
||||
result = getOldInstruction(instruction)
|
||||
.(OldIR::BuiltInOperationInstruction)
|
||||
.getBuiltInOperation()
|
||||
}
|
||||
|
||||
cached
|
||||
Type getInstructionExceptionType(Instruction instruction) {
|
||||
Language::LanguageType getInstructionExceptionType(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
|
||||
}
|
||||
|
||||
@@ -380,14 +364,9 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
int getInstructionResultSize(Instruction instruction) {
|
||||
// Only return a result for instructions that needed an explicit result size.
|
||||
instruction.getResultType() instanceof UnknownType and
|
||||
result = getOldInstruction(instruction).getResultSize()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
|
||||
predicate getInstructionInheritance(
|
||||
Instruction instruction, Language::Class baseClass, Language::Class derivedClass
|
||||
) {
|
||||
exists(OldIR::InheritanceConversionInstruction oldInstr |
|
||||
oldInstr = getOldInstruction(instruction) and
|
||||
baseClass = oldInstr.getBaseClass() and
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
@@ -2,4 +2,5 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
|
||||
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SimpleSSA as Alias
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
import AliasAnalysis
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.implementation.raw.IR
|
||||
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.Overlap
|
||||
private import SimpleSSAImports
|
||||
|
||||
private class IntValue = Ints::IntValue;
|
||||
|
||||
private predicate hasResultMemoryAccess(
|
||||
Instruction instr, IRVariable var, Type type, IntValue bitOffset
|
||||
Instruction instr, IRVariable var, Language::LanguageType type, IntValue bitOffset
|
||||
) {
|
||||
resultPointsTo(instr.getResultAddressOperand().getAnyDef(), var, bitOffset) and
|
||||
type = instr.getResultType()
|
||||
type = instr.getResultLanguageType()
|
||||
}
|
||||
|
||||
private predicate hasOperandMemoryAccess(
|
||||
MemoryOperand operand, IRVariable var, Type type, IntValue bitOffset
|
||||
MemoryOperand operand, IRVariable var, Language::LanguageType type, IntValue bitOffset
|
||||
) {
|
||||
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, bitOffset) and
|
||||
type = operand.getType()
|
||||
type = operand.getLanguageType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,17 +26,17 @@ private predicate isVariableModeled(IRVariable var) {
|
||||
not variableAddressEscapes(var) and
|
||||
// There's no need to check for the right size. An `IRVariable` never has an `UnknownType`, so the test for
|
||||
// `type = var.getType()` is sufficient.
|
||||
forall(Instruction instr, Type type, IntValue bitOffset |
|
||||
forall(Instruction instr, Language::LanguageType type, IntValue bitOffset |
|
||||
hasResultMemoryAccess(instr, var, type, bitOffset)
|
||||
|
|
||||
bitOffset = 0 and
|
||||
type = var.getType()
|
||||
type.getIRType() = var.getIRType()
|
||||
) and
|
||||
forall(MemoryOperand operand, Type type, IntValue bitOffset |
|
||||
forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset |
|
||||
hasOperandMemoryAccess(operand, var, type, bitOffset)
|
||||
|
|
||||
bitOffset = 0 and
|
||||
type = var.getType()
|
||||
type.getIRType() = var.getIRType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -59,7 +55,7 @@ class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
final VirtualVariable getVirtualVariable() { result = this }
|
||||
|
||||
final Type getType() { result = var.getType() }
|
||||
final Language::LanguageType getType() { result = var.getLanguageType() }
|
||||
|
||||
final string getUniqueId() { result = var.getUniqueId() }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR
|
||||
import semmle.code.cpp.ir.internal.IntegerConstant as Ints
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
507
cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
Normal file
507
cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
Normal file
@@ -0,0 +1,507 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.Print
|
||||
private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
|
||||
private int getPointerSize() { result = max(any(NullPointerType t).getSize()) }
|
||||
|
||||
/**
|
||||
* Works around an extractor bug where a function reference gets a size of one byte.
|
||||
*/
|
||||
private int getTypeSizeWorkaround(Type type) {
|
||||
exists(Type unspecifiedType |
|
||||
unspecifiedType = type.getUnspecifiedType() and
|
||||
(
|
||||
unspecifiedType instanceof FunctionReferenceType and
|
||||
result = getPointerSize()
|
||||
or
|
||||
exists(PointerToMemberType ptmType |
|
||||
ptmType = unspecifiedType and
|
||||
(
|
||||
if ptmType.getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = getPointerSize() * 2
|
||||
else result = getPointerSize()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ArrayType arrayType |
|
||||
// Treat `T[]` as `T*`.
|
||||
arrayType = unspecifiedType and
|
||||
not arrayType.hasArraySize() and
|
||||
result = getPointerSize()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private int getTypeSize(Type type) {
|
||||
if exists(getTypeSizeWorkaround(type))
|
||||
then result = getTypeSizeWorkaround(type)
|
||||
else result = type.getSize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `IRErrorType` should exist.
|
||||
*/
|
||||
predicate hasErrorType() { exists(ErroneousType t) }
|
||||
|
||||
/**
|
||||
* Holds if an `IRBooleanType` with the specified `byteSize` should exist.
|
||||
*/
|
||||
predicate hasBooleanType(int byteSize) { byteSize = getTypeSize(any(BoolType type)) }
|
||||
|
||||
private predicate isSigned(IntegralOrEnumType type) {
|
||||
type.(IntegralType).isSigned()
|
||||
or
|
||||
exists(Enum enumType |
|
||||
// If the enum has an explicit underlying type, we'll determine signedness from that. If not,
|
||||
// we'll assume unsigned. The actual rules for the implicit underlying type of an enum vary
|
||||
// between compilers, so we'll need an extractor change to get this 100% right. Until then,
|
||||
// unsigned is a reasonable default.
|
||||
enumType = type.getUnspecifiedType() and
|
||||
enumType.getExplicitUnderlyingType().getUnspecifiedType().(IntegralType).isSigned()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isSignedIntegerType(IntegralOrEnumType type) {
|
||||
isSigned(type) and not type instanceof BoolType
|
||||
}
|
||||
|
||||
private predicate isUnsignedIntegerType(IntegralOrEnumType type) {
|
||||
not isSigned(type) and not type instanceof BoolType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `IRSignedIntegerType` with the specified `byteSize` should exist.
|
||||
*/
|
||||
predicate hasSignedIntegerType(int byteSize) {
|
||||
byteSize = any(IntegralOrEnumType type | isSignedIntegerType(type)).getSize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `IRUnsignedIntegerType` with the specified `byteSize` should exist.
|
||||
*/
|
||||
predicate hasUnsignedIntegerType(int byteSize) {
|
||||
byteSize = any(IntegralOrEnumType type | isUnsignedIntegerType(type)).getSize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `IRFloatingPointType` with the specified `byteSize` should exist.
|
||||
*/
|
||||
predicate hasFloatingPointType(int byteSize) { byteSize = any(FloatingPointType type).getSize() }
|
||||
|
||||
private predicate isPointerIshType(Type type) {
|
||||
type instanceof PointerType
|
||||
or
|
||||
type instanceof ReferenceType
|
||||
or
|
||||
type instanceof NullPointerType
|
||||
or
|
||||
// Treat `T[]` as a pointer. The only place we should see these is as the type of a parameter. If
|
||||
// the corresponding decayed `T*` type is available, we'll use that, but if it's not available,
|
||||
// we're stuck with `T[]`. Just treat it as a pointer.
|
||||
type instanceof ArrayType and not exists(type.getSize())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `IRAddressType` with the specified `byteSize` should exist.
|
||||
*/
|
||||
predicate hasAddressType(int byteSize) {
|
||||
// This covers all pointers, all references, and because it also looks at `NullPointerType`, it
|
||||
// should always return a result that makes sense for arbitrary glvalues as well.
|
||||
byteSize = any(Type type | isPointerIshType(type)).getSize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `IRFunctionAddressType` with the specified `byteSize` should exist.
|
||||
*/
|
||||
predicate hasFunctionAddressType(int byteSize) {
|
||||
byteSize = getPointerSize() or // Covers function lvalues
|
||||
byteSize = getTypeSize(any(FunctionPointerIshType type))
|
||||
}
|
||||
|
||||
private predicate isOpaqueType(Type type) {
|
||||
exists(type.getSize()) and // Only include complete types
|
||||
(
|
||||
type instanceof ArrayType or
|
||||
type instanceof Class or
|
||||
type instanceof GNUVectorType
|
||||
)
|
||||
or
|
||||
type instanceof PointerToMemberType // PTMs are missing size info
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an `IROpaqueType` with the specified `tag` and `byteSize` should exist.
|
||||
*/
|
||||
predicate hasOpaqueType(Type tag, int byteSize) {
|
||||
isOpaqueType(tag) and byteSize = getTypeSize(tag)
|
||||
or
|
||||
tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `IRType` that represents a prvalue of the specified `Type`.
|
||||
*/
|
||||
private IRType getIRTypeForPRValue(Type type) {
|
||||
exists(Type unspecifiedType | unspecifiedType = type.getUnspecifiedType() |
|
||||
isOpaqueType(unspecifiedType) and
|
||||
exists(IROpaqueType opaqueType | opaqueType = result |
|
||||
opaqueType.getByteSize() = getTypeSize(type) and
|
||||
opaqueType.getTag() = unspecifiedType
|
||||
)
|
||||
or
|
||||
unspecifiedType instanceof BoolType and result.(IRBooleanType).getByteSize() = type.getSize()
|
||||
or
|
||||
isSignedIntegerType(unspecifiedType) and
|
||||
result.(IRSignedIntegerType).getByteSize() = type.getSize()
|
||||
or
|
||||
isUnsignedIntegerType(unspecifiedType) and
|
||||
result.(IRUnsignedIntegerType).getByteSize() = type.getSize()
|
||||
or
|
||||
unspecifiedType instanceof FloatingPointType and
|
||||
result.(IRFloatingPointType).getByteSize() = type.getSize()
|
||||
or
|
||||
isPointerIshType(unspecifiedType) and result.(IRAddressType).getByteSize() = getTypeSize(type)
|
||||
or
|
||||
unspecifiedType instanceof FunctionPointerIshType and
|
||||
result.(IRFunctionAddressType).getByteSize() = getTypeSize(type)
|
||||
or
|
||||
unspecifiedType instanceof VoidType and result instanceof IRVoidType
|
||||
or
|
||||
unspecifiedType instanceof ErroneousType and result instanceof IRErrorType
|
||||
or
|
||||
unspecifiedType instanceof UnknownType and result instanceof IRUnknownType
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TCppType =
|
||||
TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or
|
||||
TFunctionGLValueType() or
|
||||
TGLValueAddressType(Type type) or
|
||||
TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or
|
||||
TUnknownType()
|
||||
|
||||
/**
|
||||
* The C++ type of an IR entity.
|
||||
* This cannot just be `Type` for a couple reasons:
|
||||
* - Some types needed by the IR might not exist in the database (e.g. `RoutineType`s for functions
|
||||
* that are always called directly)
|
||||
* - Some types needed by the IR are not representable in the C++ type system (e.g. the result type
|
||||
* of a `VariableAddress` where the variable is of reference type)
|
||||
*/
|
||||
class CppType extends TCppType {
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets a string used in IR dumps */
|
||||
string getDumpString() { result = toString() }
|
||||
|
||||
/** Gets the size of the type in bytes, if known. */
|
||||
final int getByteSize() { result = getIRType().getByteSize() }
|
||||
|
||||
/**
|
||||
* Gets the `IRType` that represents this `CppType`. Many different `CppType`s can map to a single
|
||||
* `IRType`.
|
||||
*/
|
||||
IRType getIRType() { none() }
|
||||
|
||||
/**
|
||||
* Holds if the `CppType` represents a prvalue of type `Type` (if `isGLValue` is `false`), or if
|
||||
* it represents a glvalue of type `Type` (if `isGLValue` is `true`).
|
||||
*/
|
||||
predicate hasType(Type type, boolean isGLValue) { none() }
|
||||
|
||||
final predicate hasUnspecifiedType(Type type, boolean isGLValue) {
|
||||
exists(Type specifiedType |
|
||||
hasType(specifiedType, isGLValue) and
|
||||
type = specifiedType.getUnspecifiedType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CppType` that wraps an existing `Type` (either as a prvalue or a glvalue).
|
||||
*/
|
||||
private class CppWrappedType extends CppType {
|
||||
Type ctype;
|
||||
|
||||
CppWrappedType() {
|
||||
this = TPRValueType(ctype) or
|
||||
this = TGLValueAddressType(ctype)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CppType` that represents a prvalue of an existing `Type`.
|
||||
*/
|
||||
private class CppPRValueType extends CppWrappedType, TPRValueType {
|
||||
final override string toString() { result = ctype.toString() }
|
||||
|
||||
final override string getDumpString() { result = ctype.getUnspecifiedType().toString() }
|
||||
|
||||
final override IRType getIRType() { result = getIRTypeForPRValue(ctype) }
|
||||
|
||||
final override predicate hasType(Type type, boolean isGLValue) {
|
||||
type = ctype and
|
||||
isGLValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CppType` that has unknown type but a known size. Generally to represent synthesized types that
|
||||
* occur in certain cases during IR construction, such as the type of a zero-initialized segment of
|
||||
* a partially-initialized array.
|
||||
*/
|
||||
private class CppUnknownOpaqueType extends CppType, TUnknownOpaqueType {
|
||||
int byteSize;
|
||||
|
||||
CppUnknownOpaqueType() { this = TUnknownOpaqueType(byteSize) }
|
||||
|
||||
final override string toString() { result = "unknown[" + byteSize.toString() + "]" }
|
||||
|
||||
final override IROpaqueType getIRType() {
|
||||
result.getByteSize() = byteSize and result.getTag() instanceof UnknownType
|
||||
}
|
||||
|
||||
override predicate hasType(Type type, boolean isGLValue) {
|
||||
type instanceof UnknownType and isGLValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CppType` that represents a glvalue of an existing `Type`.
|
||||
*/
|
||||
private class CppGLValueAddressType extends CppWrappedType, TGLValueAddressType {
|
||||
final override string toString() { result = "glval<" + ctype.toString() + ">" }
|
||||
|
||||
final override string getDumpString() {
|
||||
result = "glval<" + ctype.getUnspecifiedType().toString() + ">"
|
||||
}
|
||||
|
||||
final override IRAddressType getIRType() { result.getByteSize() = getPointerSize() }
|
||||
|
||||
final override predicate hasType(Type type, boolean isGLValue) {
|
||||
type = ctype and
|
||||
isGLValue = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CppType` that represents a function lvalue.
|
||||
*/
|
||||
private class CppFunctionGLValueType extends CppType, TFunctionGLValueType {
|
||||
final override string toString() { result = "glval<unknown>" }
|
||||
|
||||
final override IRFunctionAddressType getIRType() { result.getByteSize() = getPointerSize() }
|
||||
|
||||
final override predicate hasType(Type type, boolean isGLValue) {
|
||||
type instanceof UnknownType and isGLValue = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CppType` that represents an unknown type.
|
||||
*/
|
||||
private class CppUnknownType extends CppType, TUnknownType {
|
||||
final override string toString() { result = any(UnknownType type).toString() }
|
||||
|
||||
final override IRUnknownType getIRType() { any() }
|
||||
|
||||
final override predicate hasType(Type type, boolean isGLValue) {
|
||||
type instanceof UnknownType and isGLValue = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single instance of `CppUnknownType`.
|
||||
*/
|
||||
CppUnknownType getUnknownType() { any() }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a prvalue of type `void`.
|
||||
*/
|
||||
CppPRValueType getVoidType() { exists(VoidType voidType | result.hasType(voidType, false)) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a prvalue of type `type`.
|
||||
*/
|
||||
CppType getTypeForPRValue(Type type) {
|
||||
if type instanceof UnknownType
|
||||
then result instanceof CppUnknownType
|
||||
else result.hasType(type, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a prvalue of type `type`, if such a `CppType` exists.
|
||||
* Otherwise, gets `CppUnknownType`.
|
||||
*/
|
||||
CppType getTypeForPRValueOrUnknown(Type type) {
|
||||
result = getTypeForPRValue(type)
|
||||
or
|
||||
not exists(getTypeForPRValue(type)) and result = getUnknownType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a glvalue of type `type`.
|
||||
*/
|
||||
CppType getTypeForGLValue(Type type) { result.hasType(type, true) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a prvalue of type `int`.
|
||||
*/
|
||||
CppPRValueType getIntType() {
|
||||
exists(IntType type |
|
||||
type.isImplicitlySigned() and
|
||||
result.hasType(type, false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a prvalue of type `bool`.
|
||||
*/
|
||||
CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a glvalue of function type.
|
||||
*/
|
||||
CppFunctionGLValueType getFunctionGLValueType() { any() }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a opaque of unknown type with size `byteSize`.
|
||||
*/
|
||||
CppUnknownOpaqueType getUnknownOpaqueType(int byteSize) { result.getByteSize() = byteSize }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for an `IRBooleanType` with the specified
|
||||
* `byteSize`.
|
||||
*/
|
||||
CppWrappedType getCanonicalBooleanType(int byteSize) {
|
||||
exists(BoolType type | result = TPRValueType(type) and byteSize = type.getSize())
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the sorting priority of an `IntegralType` based on its signedness.
|
||||
*/
|
||||
private int getSignPriority(IntegralType type) {
|
||||
// Explicitly unsigned types sort first. Explicitly signed types sort last. Types with no explicit
|
||||
// signedness sort in between. This lets us always choose `int` over `signed int`, while also
|
||||
// choosing `unsigned char`+`char` when `char` is signed, and `unsigned char`+`signed char` when
|
||||
// `char` is unsigned.
|
||||
if type.isExplicitlyUnsigned()
|
||||
then result = 2
|
||||
else
|
||||
if type.isExplicitlySigned()
|
||||
then result = 0
|
||||
else result = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sort priority of an `IntegralType` based on its kind.
|
||||
*/
|
||||
private int getKindPriority(IntegralType type) {
|
||||
// `CharType` sorts lower so that we prefer the plain integer types when they have the same size
|
||||
// as a `CharType`.
|
||||
if type instanceof CharType then result = 0 else result = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for an `IRSignedIntegerType` with the specified
|
||||
* `byteSize`.
|
||||
*/
|
||||
CppPRValueType getCanonicalSignedIntegerType(int byteSize) {
|
||||
result = TPRValueType(max(IntegralType type |
|
||||
type.isSigned() and type.getSize() = byteSize
|
||||
|
|
||||
type order by getKindPriority(type), getSignPriority(type), type.toString() desc
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for an `IRUnsignedIntegerType` with the specified
|
||||
* `byteSize`.
|
||||
*/
|
||||
CppPRValueType getCanonicalUnsignedIntegerType(int byteSize) {
|
||||
result = TPRValueType(max(IntegralType type |
|
||||
type.isUnsigned() and type.getSize() = byteSize
|
||||
|
|
||||
type order by getKindPriority(type), getSignPriority(type), type.toString() desc
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for an `IRFloatingPointType` with the specified
|
||||
* `byteSize`.
|
||||
*/
|
||||
CppPRValueType getCanonicalFloatingPointType(int byteSize) {
|
||||
result = TPRValueType(max(FloatingPointType type |
|
||||
type.getSize() = byteSize
|
||||
|
|
||||
type order by type.toString() desc
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for an `IRAddressType` with the specified
|
||||
* `byteSize`.
|
||||
*/
|
||||
CppPRValueType getCanonicalAddressType(int byteSize) {
|
||||
// We just use `NullPointerType`, since it should be unique.
|
||||
exists(NullPointerType type |
|
||||
type.getSize() = byteSize and
|
||||
result = TPRValueType(type)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for an `IRFunctionAddressType` with the specified
|
||||
* `byteSize`.
|
||||
*/
|
||||
CppFunctionGLValueType getCanonicalFunctionAddressType(int byteSize) {
|
||||
result.getByteSize() = byteSize
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for `IRErrorType`.
|
||||
*/
|
||||
CppPRValueType getCanonicalErrorType() { result = TPRValueType(any(ErroneousType type)) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for `IRUnknownType`.
|
||||
*/
|
||||
CppUnknownType getCanonicalUnknownType() { any() }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for `IRVoidType`.
|
||||
*/
|
||||
CppPRValueType getCanonicalVoidType() { result = TPRValueType(any(VoidType type)) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that is the canonical type for an `IROpaqueType` with the specified `tag` and
|
||||
* `byteSize`.
|
||||
*/
|
||||
CppType getCanonicalOpaqueType(Type tag, int byteSize) {
|
||||
isOpaqueType(tag) and
|
||||
result = TPRValueType(tag.getUnspecifiedType()) and
|
||||
getTypeSize(tag) = byteSize
|
||||
or
|
||||
tag instanceof UnknownType and result = getUnknownOpaqueType(byteSize)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string that uniquely identifies an `IROpaqueType` tag. This may be different from the usual
|
||||
* `toString()` of the tag in order to ensure uniqueness.
|
||||
*/
|
||||
string getOpaqueTagIdentityString(Type tag) {
|
||||
hasOpaqueType(tag, _) and
|
||||
result = getTypeIdentityString(tag)
|
||||
}
|
||||
|
||||
module LanguageTypeSanity {
|
||||
query predicate missingCppType(Type type, string message) {
|
||||
not exists(getTypeForPRValue(type)) and
|
||||
exists(type.getSize()) and
|
||||
// `ProxyClass`es have a size, but only appear in uninstantiated templates
|
||||
not type instanceof ProxyClass and
|
||||
message = "Type does not have an associated `CppType`."
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,13 @@
|
||||
private import cpp as Cpp
|
||||
private import semmle.code.cpp.Print as Print
|
||||
private import IRUtilities
|
||||
private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
import CppType
|
||||
|
||||
class LanguageType = CppType;
|
||||
|
||||
class OpaqueTypeTag = Cpp::Type;
|
||||
|
||||
class Function = Cpp::Function;
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ private module RangeAnalysisCache {
|
||||
cached
|
||||
module RangeAnalysisPublic {
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `i`.
|
||||
* Holds if `b + delta` is a valid bound for `i` and this is the best such delta.
|
||||
* - `upper = true` : `i <= b + delta`
|
||||
* - `upper = false` : `i >= b + delta`
|
||||
*
|
||||
@@ -90,11 +90,12 @@ private module RangeAnalysisCache {
|
||||
*/
|
||||
cached
|
||||
predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) {
|
||||
boundedInstruction(i, b, delta, upper, _, _, reason)
|
||||
boundedInstruction(i, b, delta, upper, _, _, reason) and
|
||||
bestInstructionBound(i, b, delta, upper)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `op`.
|
||||
* Holds if `b + delta` is a valid bound for `op` and this is the best such delta.
|
||||
* - `upper = true` : `op <= b + delta`
|
||||
* - `upper = false` : `op >= b + delta`
|
||||
*
|
||||
@@ -104,9 +105,8 @@ private module RangeAnalysisCache {
|
||||
*/
|
||||
cached
|
||||
predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) {
|
||||
boundedNonPhiOperand(op, b, delta, upper, _, _, reason)
|
||||
or
|
||||
boundedPhiOperand(op, b, delta, upper, _, _, reason)
|
||||
boundedOperandCand(op, b, delta, upper, reason) and
|
||||
bestOperandBound(op, b, delta, upper)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,43 @@ private module RangeAnalysisCache {
|
||||
private import RangeAnalysisCache
|
||||
import RangeAnalysisPublic
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e` and this is the best such delta.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bestInstructionBound(Instruction i, Bound b, int delta, boolean upper) {
|
||||
delta = min(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = true
|
||||
or
|
||||
delta = max(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `op`.
|
||||
* - `upper = true` : `op <= b + delta`
|
||||
* - `upper = false` : `op >= b + delta`
|
||||
*
|
||||
* The reason for the bound is given by `reason` and may be either a condition
|
||||
* or `NoReason` if the bound was proven directly without the use of a bounding
|
||||
* condition.
|
||||
*/
|
||||
private predicate boundedOperandCand(Operand op, Bound b, int delta, boolean upper, Reason reason) {
|
||||
boundedNonPhiOperand(op, b, delta, upper, _, _, reason)
|
||||
or
|
||||
boundedPhiOperand(op, b, delta, upper, _, _, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `op` and this is the best such delta.
|
||||
* - `upper = true` : `op <= b + delta`
|
||||
* - `upper = false` : `op >= b + delta`
|
||||
*/
|
||||
private predicate bestOperandBound(Operand op, Bound b, int delta, boolean upper) {
|
||||
delta = min(int d | boundedOperandCand(op, b, d, upper, _)) and upper = true
|
||||
or
|
||||
delta = max(int d | boundedOperandCand(op, b, d, upper, _)) and upper = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `vn` equals `bound + delta`.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user