Merge branch 'main' into rdmarsh/cpp/use-taint-configuration-dtt

This commit is contained in:
Mathias Vorreiter Pedersen
2021-03-09 12:35:44 +01:00
990 changed files with 16540 additions and 4028 deletions

View File

@@ -9,7 +9,7 @@
It is likely that these conditions indicate an error in the branching condition.
Alternatively, the conditions may have been left behind after debugging.</p>
<include src="aliasAnalysisWarning.qhelp" />
<include src="aliasAnalysisWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -13,7 +13,7 @@ If left in the code base they increase object code size, decrease code comprehen
This type of function may be part of the program's API and could be used by external programs.
</p>
<include src="callGraphWarning.qhelp" />
<include src="callGraphWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -10,7 +10,7 @@ This query looks at functions that return file or socket descriptors, but may re
This can occur when an operation performed on the open descriptor fails, and the function returns with an error before it closes the open resource. An improperly handled error could cause the function to leak resource descriptors. Failing to close resources in the function that opened them also makes it more difficult to detect leaks.
</p>
<include src="dataFlowWarning.qhelp" />
<include src="dataFlowWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -10,7 +10,7 @@ This rule finds calls to <code>socket</code> where there is no corresponding <co
Leaving descriptors open will cause a resource leak that will persist even after the program terminates.
</p>
<include src="aliasAnalysisWarning.qhelp" />
<include src="aliasAnalysisWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -10,7 +10,7 @@ This rule looks at functions that return a <code>FILE*</code>, but may return an
This can occur when an operation performed on the open descriptor fails, and the function returns with an error before closing the open resource. An improperly handled error may cause the function to leak file descriptors.
</p>
<include src="dataFlowWarning.qhelp" />
<include src="dataFlowWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -10,7 +10,7 @@ This rule finds calls to <code>fopen</code> with no corresponding <code>fclose</
Leaving files open will cause a resource leak that will persist even after the program terminates.
</p>
<include src="aliasAnalysisWarning.qhelp" />
<include src="aliasAnalysisWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -10,7 +10,7 @@ Not all compilers generate code that zero-out memory, especially when optimizati
is not compliant with the latest language standards. Accessing uninitialized memory will lead to undefined results.
</p>
<include src="dataFlowWarning.qhelp" />
<include src="dataFlowWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -12,7 +12,7 @@ Dereferencing a null pointer and attempting to modify its contents can lead to a
important system data (including the interrupt table in some architectures).
</p>
<include src="pointsToWarning.qhelp" />
<include src="pointsToWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -11,7 +11,7 @@ Uninitialized variables may contain any value, as not all compilers generate cod
optimizations are enabled or the compiler is not compliant with the latest language standards.
</p>
<include src="callGraphWarning.qhelp" />
<include src="callGraphWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -13,7 +13,7 @@ after. Otherwise, if the value is negative then the program will have failed
before performing the test.
</p>
<include src="dataFlowWarning.qhelp" />
<include src="dataFlowWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -9,7 +9,7 @@
This rule looks for functions that allocate memory, but may return without freeing it. This can occur when an operation performed on the memory block fails, and the function returns with an error before freeing the allocated block. This causes the function to leak memory and may eventually lead to software failure.
</p>
<include src="dataFlowWarning.qhelp" />
<include src="dataFlowWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -10,7 +10,7 @@ This rule finds calls to the <code>alloc</code> family of functions without a co
This leads to memory leaks.
</p>
<include src="aliasAnalysisWarning.qhelp" />
<include src="aliasAnalysisWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -16,7 +16,7 @@ buffer overruns.
The query looks only at the return values of functions that may return a negative value (not all functions).
</p>
<include src="dataFlowWarning.qhelp" />
<include src="dataFlowWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -63,7 +63,7 @@ destructors likely not be called (as previously noted), but the pointer will als
potentially less of a serious issue than that posed by the first approach, but it should still be avoided.</li>
</ul>
<include src="pointsToWarning.qhelp" />
<include src="pointsToWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -18,7 +18,7 @@ an array (which could have header data specifying the length of the array) and w
element of the 'array', which would likely lead to a segfault due to the invalid header data.
</p>
<include src="pointsToWarning.qhelp" />
<include src="pointsToWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -19,7 +19,7 @@ the data being copied. Buffer overflows can result to anything from a segmentati
if the array is on stack-allocated memory).
</p>
<include src="aliasAnalysisWarning.qhelp" />
<include src="aliasAnalysisWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -14,7 +14,7 @@ Buffer overflows can lead to anything from a segmentation fault to a security vu
Ensure that the size parameter is derived from the size of the destination buffer, and
not the source buffer.</p>
<include src="aliasAnalysisWarning.qhelp" />
<include src="aliasAnalysisWarning.inc.qhelp" />
</recommendation>

View File

@@ -12,7 +12,7 @@ the contents of that memory become undefined after that. Clearly, using a pointe
memory after the function has already returned will have undefined results.
</p>
<include src="pointsToWarning.qhelp" />
<include src="pointsToWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -12,7 +12,7 @@ from a segfault to memory corruption that would cause subsequent calls to the dy
erratically, to a possible security vulnerability.
</p>
<include src="pointsToWarning.qhelp" />
<include src="pointsToWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -2,6 +2,6 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CommentedOutCodeQuery.qhelp" />
<include src="../Metrics/Files/CommentedOutCodeReferences.qhelp" />
<include src="CommentedOutCodeQuery.inc.qhelp" />
<include src="../Metrics/Files/CommentedOutCodeReferences.inc.qhelp" />
</qhelp>

View File

@@ -3,7 +3,7 @@
"qhelp.dtd">
<qhelp>
<overview>
<include src="LeapYear.qhelp" />
<include src="LeapYear.inc.qhelp" />
<p>When performing arithmetic operations on a variable that represents a date, leap years must be taken into account.
It is not safe to assume that a year is 365 days long.</p>

View File

@@ -3,7 +3,7 @@
"qhelp.dtd">
<qhelp>
<overview>
<include src="LeapYear.qhelp" />
<include src="LeapYear.inc.qhelp" />
<p>When performing arithmetic operations on a variable that represents a year, it is important to consider that the resulting value may not be a valid date.</p>
<p>The typical example is doing simple year arithmetic (i.e. <code>date.year++</code>) without considering if the resulting value will be a valid date or not.</p>

View File

@@ -3,7 +3,7 @@
"qhelp.dtd">
<qhelp>
<overview>
<include src="LeapYear.qhelp" />
<include src="LeapYear.inc.qhelp" />
<p>When using a function that transforms a date structure, and the year on the input argument for the API has been manipulated, it is important to check for the return value of the function to make sure it succeeded.</p>
<p>Otherwise, the function may have failed, and the output parameter may contain invalid data that can cause any number of problems on the affected system.</p>

View File

@@ -3,7 +3,7 @@
"qhelp.dtd">
<qhelp>
<overview>
<include src="LeapYear.qhelp" />
<include src="LeapYear.inc.qhelp" />
<p>This query helps to detect when a developer allocates an array or other fixed-length data structure such as <code>std::vector</code> with 365 elements one for each day of the year.</p>
<p>Since leap years have 366 days, there will be no allocated element on December 31st at the end of a leap year; which will lead to a buffer overflow on a leap year.</p>

View File

@@ -2,6 +2,6 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CommentedOutCodeMetricOverview.qhelp" />
<include src="CommentedOutCodeReferences.qhelp" />
<include src="CommentedOutCodeMetricOverview.inc.qhelp" />
<include src="CommentedOutCodeReferences.inc.qhelp" />
</qhelp>

View File

@@ -2,5 +2,5 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="FLinesOfDuplicatedCodeCommon.qhelp" />
<include src="FLinesOfDuplicatedCodeCommon.inc.qhelp" />
</qhelp>

View File

@@ -14,7 +14,7 @@ for a number of reasons.
</p>
</overview>
<include src="DuplicationProblems.qhelp" />
<include src="DuplicationProblems.inc.qhelp" />
<recommendation>

View File

@@ -2,4 +2,4 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CleartextStorage.qhelp" /></qhelp>
<include src="CleartextStorage.inc.qhelp" /></qhelp>

View File

@@ -2,4 +2,4 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CleartextStorage.qhelp" /></qhelp>
<include src="CleartextStorage.inc.qhelp" /></qhelp>

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query highlights calls to the standard library functions <code>abort, exit, getenv</code> and <code>system</code>.

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query ensures that all operators with opposites (e.g. == and !=) are both defined, and

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query highlights return statements that return pointers to an object allocated on the stack. The lifetime
@@ -18,7 +18,7 @@ memory after the function has already returned will have undefined results.
<!-- Mention how the results could be probabilistic (uses pointsto) -->
<include src="../../Critical/pointsToWarning.qhelp" />
<include src="../../Critical/pointsToWarning.inc.qhelp" />
</overview>
<recommendation>

View File

@@ -12,7 +12,7 @@ calling convention for x86, it would be whatever value was in the AX/EAX registe
assuming the function had a non-float return type that can fit in a machine word.
</p>
<include src="../../Critical/dataFlowWarning.qhelp" />
<include src="../../Critical/dataFlowWarning.inc.qhelp" />
<!--/*FALSEPOSITIVE_WARNING*/-->

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query highlights identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope.

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query highlights variables with the <code>register</code> storage class specifier. Modern compilers are now capable of

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query highlights portions of code that can expose the floating point implementation of the underlying

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query highlights string literals that are assigned to a non-<code>const</code> variable. String literals

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query finds bit fields with members that are not explicitly declared to be unsigned.

View File

@@ -7,7 +7,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>
This query finds unsigned values that are being negated. Behavior is undefined in such cases.

View File

@@ -6,7 +6,7 @@
<overview>
<!-- Mention that this rule may not be applicable in projects that don't follow the JSF standard. -->
<include src="../jsfNote.qhelp" />
<include src="../jsfNote.inc.qhelp" />
<p>Use of goto statements makes code more difficult to understand and maintain. Consequently, the use
of goto statements is deprecated except as a mechanism for breaking out of multiple nested loops.

View File

@@ -80,11 +80,9 @@ class Element extends ElementBase {
File getFile() { result = this.getLocation().getFile() }
/**
* Holds if this element may be from source.
*
* Note: this predicate is provided for consistency with the libraries
* for other languages, such as Java and Python. In C++, all files are
* classified as source files, so this predicate is always true.
* Holds if this element may be from source. This predicate holds for all
* elements, except for those in the dummy file, whose name is the empty string.
* The dummy file contains declarations that are built directly into the compiler.
*/
predicate fromSource() { this.getFile().fromSource() }

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -415,6 +415,30 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
/**
* Holds if data can flow from `fromNode` to `toNode` because they are the post-update
* nodes of some function output and input respectively, where the output and input
* are aliases. A typical example is a function returning `this`, implementing a fluent
* interface.
*/
cached
predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
exists(Node fromPre, Node toPre |
fromPre = fromNode.getPreUpdateNode() and
toPre = toNode.getPreUpdateNode()
|
exists(DataFlowCall c |
// Does the language-specific simpleLocalFlowStep already model flow
// from function input to output?
fromPre = getAnOutNode(c, _) and
toPre.(ArgumentNode).argumentOf(c, _) and
simpleLocalFlowStep(toPre.(ArgumentNode), fromPre)
)
or
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
)
}
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -223,7 +223,10 @@ private predicate isAdditionalFlowStep(
* Holds if data can flow in one local step from `node1` to `node2`.
*/
private predicate localFlowStep(Node node1, Node node2, Configuration config) {
simpleLocalFlowStep(node1, node2) and
(
simpleLocalFlowStep(node1, node2) or
reverseStepThroughInputOutputAlias(node1, node2)
) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
not fullBarrier(node1, config) and

View File

@@ -415,6 +415,30 @@ private module Cached {
store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
}
/**
* Holds if data can flow from `fromNode` to `toNode` because they are the post-update
* nodes of some function output and input respectively, where the output and input
* are aliases. A typical example is a function returning `this`, implementing a fluent
* interface.
*/
cached
predicate reverseStepThroughInputOutputAlias(PostUpdateNode fromNode, PostUpdateNode toNode) {
exists(Node fromPre, Node toPre |
fromPre = fromNode.getPreUpdateNode() and
toPre = toNode.getPreUpdateNode()
|
exists(DataFlowCall c |
// Does the language-specific simpleLocalFlowStep already model flow
// from function input to output?
fromPre = getAnOutNode(c, _) and
toPre.(ArgumentNode).argumentOf(c, _) and
simpleLocalFlowStep(toPre.(ArgumentNode), fromPre)
)
or
argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre)
)
}
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.

View File

@@ -526,4 +526,4 @@ predicate isImmutableOrUnobservable(Node n) {
}
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { n instanceof OperandNode }
predicate nodeIsHidden(Node n) { n instanceof OperandNode and not n instanceof ArgumentNode }

View File

@@ -693,7 +693,11 @@ private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo
exists(ChiInstruction chi | chi = iTo |
opFrom.getAnyDef() instanceof WriteSideEffectInstruction and
chi.getPartialOperand() = opFrom and
not chi.isResultConflated()
not chi.isResultConflated() and
// In a call such as `set_value(&x->val);` we don't want the memory representing `x` to receive
// dataflow by a simple step. Instead, this is handled by field flow. If we add a simple step here
// we can get field-to-object flow.
not chi.isPartialUpdate()
)
or
// Flow through modeled functions

View File

@@ -2055,6 +2055,13 @@ class ChiInstruction extends Instruction {
final predicate getUpdatedInterval(int startBit, int endBit) {
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
}
/**
* Holds if the `ChiPartialOperand` totally, but not exactly, overlaps with the `ChiTotalOperand`.
* This means that the `ChiPartialOperand` will not override the entire memory associated with the
* `ChiTotalOperand`.
*/
final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
}
/**

View File

@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
private import Imports::IRType
private import Imports::Overlap
private import Imports::OperandTag
cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
private import Imports::TOperand
private import internal.OperandInternal
/**
* Base class for all register operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
* of `TOperand` that are used in this stage.
*/
private class RegisterOperandBase extends TRegisterOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the register operand with the specified parameters.
*/
private RegisterOperandBase registerOperand(
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
) {
result = TRegisterOperand(useInstr, tag, defInstr)
}
/**
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
* will eventually use for this purpose.
*/
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the non-Phi memory operand with the specified parameters.
*/
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
result = TNonPhiMemoryOperand(useInstr, tag)
}
/**
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
*/
private class PhiOperandBase extends TPhiOperand {
abstract string toString();
}
/**
* Returns the Phi operand with the specified parameters.
*/
private PhiOperandBase phiOperand(
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
private class TStageOperand =
TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
/**
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
* (the defining instruction) in another instruction (the use instruction)
*/
class Operand extends TOperand {
class Operand extends TStageOperand {
cached
Operand() {
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
this = phiOperand(use, def, predecessorBlock, _)
) or
exists(Instruction use | this = chiOperand(use, _))
}
/** Gets a textual representation of this element. */
string toString() { result = "Operand" }
@@ -238,9 +191,11 @@ class Operand extends TOperand {
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
*/
class MemoryOperand extends Operand {
cached
MemoryOperand() {
this instanceof NonPhiMemoryOperandBase or
this instanceof PhiOperandBase
this instanceof TNonSSAMemoryOperand or
this instanceof TPhiOperand or
this instanceof TChiOperand
}
/**
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
NonPhiOperand() {
this = registerOperand(useInstr, tag, _) or
this = nonPhiMemoryOperand(useInstr, tag)
this = nonSSAMemoryOperand(useInstr, tag) or
this = chiOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
Instruction defInstr;
cached
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
/**
* A memory operand other than the operand of a `Phi` instruction.
*/
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
cached
NonPhiMemoryOperand() {
this = nonSSAMemoryOperand(useInstr, tag)
or
this = chiOperand(useInstr, tag)
}
final override string toString() { result = tag.toString() }
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
class PhiInputOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
cached
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }

View File

@@ -629,7 +629,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
}
/** Gets the start bit offset of a `MemoryLocation`, if any. */
int getStartBitOffset(VariableMemoryLocation location) { result = location.getStartBitOffset() }
int getStartBitOffset(VariableMemoryLocation location) {
result = location.getStartBitOffset() and Ints::hasValue(result)
}
/** Gets the end bit offset of a `MemoryLocation`, if any. */
int getEndBitOffset(VariableMemoryLocation location) { result = location.getEndBitOffset() }
int getEndBitOffset(VariableMemoryLocation location) {
result = location.getEndBitOffset() and Ints::hasValue(result)
}

View File

@@ -2,3 +2,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
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand

View File

@@ -0,0 +1,2 @@
private import semmle.code.cpp.ir.implementation.internal.TOperand
import AliasedSSAOperands

View File

@@ -6,6 +6,7 @@ private import Imports::Overlap
private import Imports::TInstruction
private import Imports::RawIR as RawIR
private import SSAInstructions
private import SSAOperands
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -177,6 +178,22 @@ private module Cached {
)
}
/**
* Holds if the `ChiPartialOperand` only partially overlaps with the `ChiTotalOperand`.
* This means that the `ChiPartialOperand` will not override the entire memory associated
* with the `ChiTotalOperand`.
*/
cached
predicate chiOnlyPartiallyUpdatesLocation(ChiInstruction chi) {
exists(Alias::MemoryLocation location, OldInstruction oldInstruction |
oldInstruction = getOldInstruction(chi.getPartial()) and
location = Alias::getResultMemoryLocation(oldInstruction)
|
Alias::getStartBitOffset(location) != 0 or
Alias::getEndBitOffset(location) != 8 * location.getType().getByteSize()
)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.

View File

@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand

View File

@@ -5,3 +5,4 @@ import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import AliasedSSA as Alias
import semmle.code.cpp.ir.implementation.internal.TOperand::AliasedSSAOperands as SSAOperands

View File

@@ -0,0 +1,180 @@
private import TInstruction
private import OperandTag
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawConstruction
private import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedConstruction
private import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedConstruction
private import semmle.code.cpp.ir.implementation.raw.IR as Raw
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as Unaliased
private import semmle.code.cpp.ir.implementation.aliased_ssa.IR as Aliased
private import semmle.code.cpp.ir.internal.Overlap
/**
* Provides the newtype used to represent operands across all phases of the IR.
*/
private module Internal {
/**
* An IR operand. `TOperand` is shared across all phases of the IR. There are branches of this
* type for operands created directly from the AST (`TRegisterOperand` and `TNonSSAMemoryOperand`),
* for operands computed by each stage of SSA construction (`T*PhiOperand` and
* `TAliasedChiOperand`), and a placehold branch for operands that do not exist in a given
* stage of IR construction (`TNoOperand`).
*/
cached
newtype TOperand =
// RAW
TRegisterOperand(TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr) {
defInstr = RawConstruction::getRegisterOperandDefinition(useInstr, tag) and
not RawConstruction::isInCycle(useInstr) and
strictcount(RawConstruction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
// Placeholder for Phi and Chi operands in stages that don't have the corresponding instructions
TNoOperand() { none() } or
// Can be "removed" later when there's unreachable code
// These operands can be reused across all three stages. They just get different defs.
TNonSSAMemoryOperand(Raw::Instruction useInstr, MemoryOperandTag tag) {
// Has no definition in raw but will get definitions later
useInstr.getOpcode().hasOperand(tag)
} or
TUnaliasedPhiOperand(
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
Unaliased::IRBlock predecessorBlock, Overlap overlap
) {
defInstr = UnaliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
} or
//// ALIASED
////
// Until we share SSA, these will be all the phis there are. With SSA
// sharing, these will add to the ones that are already there.
// If we share SSA, be careful with the case where we remove all possible
// indirect writes to a variable because they're dead code. In that case it's
// important that we use the same definition of "is variable aliased" across
// the phases.
TAliasedPhiOperand(
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
Aliased::IRBlock predecessorBlock, Overlap overlap
) {
defInstr = AliasedConstruction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
} or
TAliasedChiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) { any() }
}
/**
* Reexports some branches from `TOperand` so they can be used in stage modules without importing
* `TOperand` itself.
*/
private module Shared {
class TRegisterOperand = Internal::TRegisterOperand;
/**
* Returns the register operand with the specified parameters.
*/
TRegisterOperand registerOperand(
TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr
) {
result = Internal::TRegisterOperand(useInstr, tag, defInstr)
}
class TNonSSAMemoryOperand = Internal::TNonSSAMemoryOperand;
/**
* Returns the non-Phi memory operand with the specified parameters.
*/
TNonSSAMemoryOperand nonSSAMemoryOperand(TRawInstruction useInstr, MemoryOperandTag tag) {
result = Internal::TNonSSAMemoryOperand(useInstr, tag)
}
}
/**
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
* raw IR stage.
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
* a class alias.
*/
module RawOperands {
import Shared
class TPhiOperand = Internal::TNoOperand;
class TChiOperand = Internal::TNoOperand;
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
/**
* Returns the Phi operand with the specified parameters.
*/
TPhiOperand phiOperand(
Raw::PhiInstruction useInstr, Raw::Instruction defInstr, Raw::IRBlock predecessorBlock,
Overlap overlap
) {
none()
}
/**
* Returns the Chi operand with the specified parameters.
*/
TChiOperand chiOperand(Raw::Instruction useInstr, ChiOperandTag tag) { none() }
}
/**
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
* unaliased SSA stage.
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
* a class alias.
*/
module UnaliasedSSAOperands {
import Shared
class TPhiOperand = Internal::TUnaliasedPhiOperand;
class TChiOperand = Internal::TNoOperand;
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
/**
* Returns the Phi operand with the specified parameters.
*/
TPhiOperand phiOperand(
Unaliased::PhiInstruction useInstr, Unaliased::Instruction defInstr,
Unaliased::IRBlock predecessorBlock, Overlap overlap
) {
result = Internal::TUnaliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
/**
* Returns the Chi operand with the specified parameters.
*/
TChiOperand chiOperand(Unaliased::Instruction useInstr, ChiOperandTag tag) { none() }
}
/**
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the
* asliased SSA stage.
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
* a class alias.
*/
module AliasedSSAOperands {
import Shared
class TPhiOperand = Internal::TAliasedPhiOperand;
class TChiOperand = Internal::TAliasedChiOperand;
class TNonPhiMemoryOperand = TNonSSAMemoryOperand or TChiOperand;
/**
* Returns the Phi operand with the specified parameters.
*/
TPhiOperand phiOperand(
TAliasedSSAPhiInstruction useInstr, Aliased::Instruction defInstr,
Aliased::IRBlock predecessorBlock, Overlap overlap
) {
result = Internal::TAliasedPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
/**
* Returns the Chi operand with the specified parameters.
*/
TChiOperand chiOperand(TAliasedSSAChiInstruction useInstr, ChiOperandTag tag) {
result = Internal::TAliasedChiOperand(useInstr, tag)
}
}

View File

@@ -2055,6 +2055,13 @@ class ChiInstruction extends Instruction {
final predicate getUpdatedInterval(int startBit, int endBit) {
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
}
/**
* Holds if the `ChiPartialOperand` totally, but not exactly, overlaps with the `ChiTotalOperand`.
* This means that the `ChiPartialOperand` will not override the entire memory associated with the
* `ChiTotalOperand`.
*/
final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
}
/**

View File

@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
private import Imports::IRType
private import Imports::Overlap
private import Imports::OperandTag
cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
private import Imports::TOperand
private import internal.OperandInternal
/**
* Base class for all register operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
* of `TOperand` that are used in this stage.
*/
private class RegisterOperandBase extends TRegisterOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the register operand with the specified parameters.
*/
private RegisterOperandBase registerOperand(
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
) {
result = TRegisterOperand(useInstr, tag, defInstr)
}
/**
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
* will eventually use for this purpose.
*/
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the non-Phi memory operand with the specified parameters.
*/
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
result = TNonPhiMemoryOperand(useInstr, tag)
}
/**
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
*/
private class PhiOperandBase extends TPhiOperand {
abstract string toString();
}
/**
* Returns the Phi operand with the specified parameters.
*/
private PhiOperandBase phiOperand(
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
private class TStageOperand =
TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
/**
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
* (the defining instruction) in another instruction (the use instruction)
*/
class Operand extends TOperand {
class Operand extends TStageOperand {
cached
Operand() {
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
this = phiOperand(use, def, predecessorBlock, _)
) or
exists(Instruction use | this = chiOperand(use, _))
}
/** Gets a textual representation of this element. */
string toString() { result = "Operand" }
@@ -238,9 +191,11 @@ class Operand extends TOperand {
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
*/
class MemoryOperand extends Operand {
cached
MemoryOperand() {
this instanceof NonPhiMemoryOperandBase or
this instanceof PhiOperandBase
this instanceof TNonSSAMemoryOperand or
this instanceof TPhiOperand or
this instanceof TChiOperand
}
/**
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
NonPhiOperand() {
this = registerOperand(useInstr, tag, _) or
this = nonPhiMemoryOperand(useInstr, tag)
this = nonSSAMemoryOperand(useInstr, tag) or
this = chiOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
Instruction defInstr;
cached
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
/**
* A memory operand other than the operand of a `Phi` instruction.
*/
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
cached
NonPhiMemoryOperand() {
this = nonSSAMemoryOperand(useInstr, tag)
or
this = chiOperand(useInstr, tag)
}
final override string toString() { result = tag.toString() }
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
class PhiInputOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
cached
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }

View File

@@ -194,6 +194,8 @@ predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBit, int endBit)
*/
predicate getUsedInterval(Operand operand, int startBit, int endBit) { none() }
predicate chiOnlyPartiallyUpdatesLocation(ChiInstruction chi) { none() }
/** Gets a non-phi instruction that defines an operand of `instr`. */
private Instruction getNonPhiOperandDef(Instruction instr) {
result = getRegisterOperandDefinition(instr, _)

View File

@@ -2,3 +2,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
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand

View File

@@ -0,0 +1,2 @@
private import semmle.code.cpp.ir.implementation.internal.TOperand
import RawOperands

View File

@@ -2055,6 +2055,13 @@ class ChiInstruction extends Instruction {
final predicate getUpdatedInterval(int startBit, int endBit) {
Construction::getIntervalUpdatedByChi(this, startBit, endBit)
}
/**
* Holds if the `ChiPartialOperand` totally, but not exactly, overlaps with the `ChiTotalOperand`.
* This means that the `ChiPartialOperand` will not override the entire memory associated with the
* `ChiTotalOperand`.
*/
final predicate isPartialUpdate() { Construction::chiOnlyPartiallyUpdatesLocation(this) }
}
/**

View File

@@ -10,79 +10,32 @@ private import Imports::MemoryAccessKind
private import Imports::IRType
private import Imports::Overlap
private import Imports::OperandTag
cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
private import Imports::TOperand
private import internal.OperandInternal
/**
* Base class for all register operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
* An operand of an `Instruction` in this stage of the IR. Implemented as a union of the branches
* of `TOperand` that are used in this stage.
*/
private class RegisterOperandBase extends TRegisterOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the register operand with the specified parameters.
*/
private RegisterOperandBase registerOperand(
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
) {
result = TRegisterOperand(useInstr, tag, defInstr)
}
/**
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
* will eventually use for this purpose.
*/
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the non-Phi memory operand with the specified parameters.
*/
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
result = TNonPhiMemoryOperand(useInstr, tag)
}
/**
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
*/
private class PhiOperandBase extends TPhiOperand {
abstract string toString();
}
/**
* Returns the Phi operand with the specified parameters.
*/
private PhiOperandBase phiOperand(
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
private class TStageOperand =
TRegisterOperand or TNonSSAMemoryOperand or TPhiOperand or TChiOperand;
/**
* An operand of an `Instruction`. The operand represents a use of the result of one instruction
* (the defining instruction) in another instruction (the use instruction)
*/
class Operand extends TOperand {
class Operand extends TStageOperand {
cached
Operand() {
// Ensure that the operand does not refer to instructions from earlier stages that are unreachable here
exists(Instruction use, Instruction def | this = registerOperand(use, _, def)) or
exists(Instruction use | this = nonSSAMemoryOperand(use, _)) or
exists(Instruction use, Instruction def, IRBlock predecessorBlock |
this = phiOperand(use, def, predecessorBlock, _)
) or
exists(Instruction use | this = chiOperand(use, _))
}
/** Gets a textual representation of this element. */
string toString() { result = "Operand" }
@@ -238,9 +191,11 @@ class Operand extends TOperand {
* An operand that consumes a memory result (e.g. the `LoadOperand` on a `Load` instruction).
*/
class MemoryOperand extends Operand {
cached
MemoryOperand() {
this instanceof NonPhiMemoryOperandBase or
this instanceof PhiOperandBase
this instanceof TNonSSAMemoryOperand or
this instanceof TPhiOperand or
this instanceof TChiOperand
}
/**
@@ -278,7 +233,8 @@ class NonPhiOperand extends Operand {
NonPhiOperand() {
this = registerOperand(useInstr, tag, _) or
this = nonPhiMemoryOperand(useInstr, tag)
this = nonSSAMemoryOperand(useInstr, tag) or
this = chiOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
@@ -298,10 +254,11 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
override RegisterOperandTag tag;
Instruction defInstr;
cached
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
@@ -317,10 +274,15 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
/**
* A memory operand other than the operand of a `Phi` instruction.
*/
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
override MemoryOperandTag tag;
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
cached
NonPhiMemoryOperand() {
this = nonSSAMemoryOperand(useInstr, tag)
or
this = chiOperand(useInstr, tag)
}
final override string toString() { result = tag.toString() }
@@ -462,12 +424,13 @@ class SideEffectOperand extends TypedOperand {
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
class PhiInputOperand extends MemoryOperand, TPhiOperand {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
cached
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }

View File

@@ -2,3 +2,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
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand

View File

@@ -0,0 +1,2 @@
private import semmle.code.cpp.ir.implementation.internal.TOperand
import UnaliasedSSAOperands

View File

@@ -6,6 +6,7 @@ private import Imports::Overlap
private import Imports::TInstruction
private import Imports::RawIR as RawIR
private import SSAInstructions
private import SSAOperands
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -177,6 +178,22 @@ private module Cached {
)
}
/**
* Holds if the `ChiPartialOperand` only partially overlaps with the `ChiTotalOperand`.
* This means that the `ChiPartialOperand` will not override the entire memory associated
* with the `ChiTotalOperand`.
*/
cached
predicate chiOnlyPartiallyUpdatesLocation(ChiInstruction chi) {
exists(Alias::MemoryLocation location, OldInstruction oldInstruction |
oldInstruction = getOldInstruction(chi.getPartial()) and
location = Alias::getResultMemoryLocation(oldInstruction)
|
Alias::getStartBitOffset(location) != 0 or
Alias::getEndBitOffset(location) != 8 * location.getType().getByteSize()
)
}
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.

View File

@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
import semmle.code.cpp.ir.implementation.raw.IR as RawIR
import semmle.code.cpp.ir.implementation.internal.TOperand as TOperand

View File

@@ -6,3 +6,4 @@ import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SimpleSSA as Alias
import semmle.code.cpp.ir.implementation.internal.TOperand::UnaliasedSSAOperands as SSAOperands