mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'master' into master
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
[*.{ql,qll,qlref,dbscheme,qhelp}]
|
||||
[*.{ql,qll,qlref,dbscheme,qhelp,html,js,mjs,ts,json,yml}]
|
||||
end_of_line = lf
|
||||
|
||||
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -15,3 +15,9 @@
|
||||
*.qlref eol=lf
|
||||
*.dbscheme eol=lf
|
||||
*.qhelp eol=lf
|
||||
*.html eol=lf
|
||||
*.js eol=lf
|
||||
*.mjs eol=lf
|
||||
*.ts eol=lf
|
||||
*.json eol=lf
|
||||
*.yml eol=lf
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -9,6 +9,6 @@
|
||||
*/ql/test/**/*.testproj
|
||||
*/ql/test/**/*.actual
|
||||
/.vs/slnx.sqlite
|
||||
/.vs/ql3/v15/Browse.VC.opendb
|
||||
/.vs/ql3/v15/Browse.VC.db
|
||||
/.vs/ql/v15/Browse.VC.opendb
|
||||
/.vs/ql/v15/Browse.VC.db
|
||||
/.vs/ProjectSettings.json
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
/csharp/ @Semmle/cs
|
||||
/java/ @Semmle/java
|
||||
/javascript/ @Semmle/js
|
||||
|
||||
20
change-notes/1.19/analysis-cpp.md
Normal file
20
change-notes/1.19/analysis-cpp.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Improvements to C/C++ analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| *@name of query (Query ID)* | *Tags* |*Aim of the new query and whether it is enabled by default or not* |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* |
|
||||
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* Added a hash consing library for structural comparison of expressions.
|
||||
@@ -2,17 +2,25 @@
|
||||
|
||||
## General improvements
|
||||
|
||||
* Modelling of taint flow through array operations has been improved. This may give additional results for the security queries.
|
||||
|
||||
* Support for popular libraries has been improved. Consequently, queries may produce more results on code bases that use the following features:
|
||||
- file system access, for example through [fs-extra](https://github.com/jprichardson/node-fs-extra) or [globby](https://www.npmjs.com/package/globby)
|
||||
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| *@name of query (Query ID)* | *Tags* |*Aim of the new query and whether it is enabled by default or not* |
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Enabling Node.js integration for Electron web content renderers (`js/enabling-electron-renderer-node-integration`) | security, frameworks/electron, external/cwe/cwe-094 | Highlights Electron web content renderer preferences with Node.js integration enabled, indicating a violation of [CWE-94](https://cwe.mitre.org/data/definitions/94.html). Results are not shown on LGTM by default. |
|
||||
| Stored cross-site scripting (`js/stored-xss`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights uncontrolled stored values flowing into HTML content, indicating a violation of [CWE-079](https://cwe.mitre.org/data/definitions/79.html). Results shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|----------------------------|----------------------------------------------|
|
||||
| Regular expression injection | Fewer false-positive results | This rule now identifies calls to `String.prototype.search` with more precision. |
|
||||
|
||||
| Unbound event handler receiver | Fewer false-positive results | This rule now recognizes additional ways class methods can be bound. |
|
||||
| Remote property injection | Fewer results | The precision of this rule has been revised to "medium". Results are no longer shown on LGTM by default. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
@@ -54,5 +54,10 @@
|
||||
"C++ SSA SSAConstruction": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ from BufferAccess ba, string bufferDesc, int accessSize, int accessType,
|
||||
where accessSize = ba.getSize()
|
||||
and bufferSize = getBufferSize(ba.getBuffer(bufferDesc, accessType),
|
||||
bufferAlloc)
|
||||
and accessSize > bufferSize
|
||||
and (accessSize > bufferSize or (accessSize <= 0 and accessType = 3))
|
||||
and if accessType = 1 then (
|
||||
message = "This '" + ba.getName() + "' operation accesses "
|
||||
+ plural(accessSize, " byte", " bytes")
|
||||
@@ -41,8 +41,13 @@ where accessSize = ba.getSize()
|
||||
+ " but the $@ is only "
|
||||
+ plural(bufferSize, " byte", " bytes") + "."
|
||||
) else (
|
||||
message = "This array indexing operation accesses byte offset "
|
||||
+ (accessSize - 1) + " but the $@ is only "
|
||||
+ plural(bufferSize, " byte", " bytes") + "."
|
||||
if accessSize > 0 then (
|
||||
message = "This array indexing operation accesses byte offset "
|
||||
+ (accessSize - 1) + " but the $@ is only "
|
||||
+ plural(bufferSize, " byte", " bytes") + "."
|
||||
) else (
|
||||
message = "This array indexing operation accesses a negative index "
|
||||
+ ((accessSize/ba.getActualType().getSize()) - 1) + " on the $@."
|
||||
)
|
||||
)
|
||||
select ba, message, bufferAlloc, bufferDesc
|
||||
|
||||
@@ -14,6 +14,7 @@ import semmle.code.cpp.dataflow.DataFlow2
|
||||
import semmle.code.cpp.dataflow.DataFlow3
|
||||
import semmle.code.cpp.dataflow.DataFlow4
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
from File f, string tag
|
||||
where none()
|
||||
|
||||
1
cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
Normal file
1
cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
Normal file
@@ -0,0 +1 @@
|
||||
import implementation.aliased_ssa.PrintIR
|
||||
1
cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll
Normal file
1
cpp/ql/src/semmle/code/cpp/ir/ValueNumbering.qll
Normal file
@@ -0,0 +1 @@
|
||||
import implementation.aliased_ssa.gvn.ValueNumbering
|
||||
@@ -5,3 +5,30 @@ import IRVariable
|
||||
import OperandTag
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* Class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
string toString() {
|
||||
result = "IRPropertyProvider"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,14 @@ module InstructionSanity {
|
||||
operand = op.getOperand(tag) and
|
||||
operand.getFunctionIR() != op.getFunctionIR()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,6 +309,13 @@ class Instruction extends Construction::TInstruction {
|
||||
result = ast.getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Expr` whose results is computed by this instruction, if any.
|
||||
*/
|
||||
final Expr getResultExpression() {
|
||||
result = Construction::getInstructionResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -554,6 +569,15 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes the `this` pointer parameter of the enclosing function.
|
||||
*/
|
||||
class InitializeThisInstruction extends Instruction {
|
||||
InitializeThisInstruction() {
|
||||
opcode instanceof Opcode::InitializeThis
|
||||
}
|
||||
}
|
||||
|
||||
class FieldAddressInstruction extends FieldInstruction {
|
||||
FieldAddressInstruction() {
|
||||
opcode instanceof Opcode::FieldAddress
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
private import IR
|
||||
import cpp
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getInstructionProperty(instr, key)
|
||||
)
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getBlockProperty(block, key)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableFunctionIR(FunctionIR funcIR) or
|
||||
TPrintableIRBlock(IRBlock block) or
|
||||
@@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
result.getFunctionIR() = block.getFunctionIR()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() {
|
||||
result = block
|
||||
}
|
||||
@@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
import cpp
|
||||
private import IR
|
||||
|
||||
/**
|
||||
* Provides additional information about value numbering in IR dumps.
|
||||
*/
|
||||
class ValueNumberPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
exists(ValueNumber vn |
|
||||
vn = valueNumber(instr) and
|
||||
key = "valnum" and
|
||||
if strictcount(vn.getAnInstruction()) > 1 then
|
||||
result = vn.toString()
|
||||
else
|
||||
result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
variableAddressValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
initializeParameterValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(FunctionIR funcIR) {
|
||||
initializeThisValueNumber(_, funcIR)
|
||||
} or
|
||||
TConstantValueNumber(FunctionIR funcIR, Type type, string value) {
|
||||
constantValueNumber(_, funcIR, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, funcIR, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) {
|
||||
unaryValueNumber(_, funcIR, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(FunctionIR funcIR, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand) {
|
||||
inheritanceConversionValueNumber(_, funcIR, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
TUniqueValueNumber(FunctionIR funcIR, Instruction instr) {
|
||||
uniqueValueNumber(instr, funcIR)
|
||||
}
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() {
|
||||
result = getExampleInstruction().getResultId()
|
||||
}
|
||||
|
||||
final Location getLocation() {
|
||||
result = getExampleInstruction().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
* least one result.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
this = valueNumber(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result = min(Instruction instr |
|
||||
instr = getAnInstruction() |
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
* For example:
|
||||
* ```
|
||||
* Point p = { 1, 2 };
|
||||
* Point q = p;
|
||||
* int a = p.x;
|
||||
* ```
|
||||
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
|
||||
* definition because it accesses the exact same memory.
|
||||
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
|
||||
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
|
||||
*
|
||||
* This concept should probably be exposed in the public IR API.
|
||||
*/
|
||||
private class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
exists(Instruction def |
|
||||
def = this.getSourceValue() and
|
||||
(
|
||||
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
|
||||
not def.hasMemoryResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this library knows how to assign a value number to the specified instruction, other than
|
||||
* a `unique` value number that is never shared by multiple instructions.
|
||||
*/
|
||||
private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof VariableAddressInstruction or
|
||||
instr instanceof InitializeParameterInstruction or
|
||||
instr instanceof InitializeThisInstruction or
|
||||
instr instanceof ConstantInstruction or
|
||||
instr instanceof FieldAddressInstruction or
|
||||
instr instanceof BinaryInstruction or
|
||||
instr instanceof UnaryInstruction or
|
||||
instr instanceof PointerArithmeticInstruction or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR,
|
||||
IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(InitializeParameterInstruction instr,
|
||||
FunctionIR funcIR, IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type,
|
||||
string value) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR,
|
||||
Field field, ValueNumber objectAddress) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getField() = field and
|
||||
valueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof PointerArithmeticInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof InheritanceConversionInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(InheritanceConversionInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` should be assigned a unique value number because this library does not know how
|
||||
* to determine if two instances of that instruction are equivalent.
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr.getResultType() instanceof VoidType) and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr) or
|
||||
exists(FunctionIR funcIR |
|
||||
uniqueValueNumber(instr, funcIR) and
|
||||
result = TUniqueValueNumber(funcIR, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(FunctionIR funcIR |
|
||||
funcIR = instr.getFunctionIR() and
|
||||
(
|
||||
exists(IRVariable var |
|
||||
variableAddressValueNumber(instr, funcIR, var) and
|
||||
result = TVariableAddressValueNumber(funcIR, var)
|
||||
) or
|
||||
exists(IRVariable var |
|
||||
initializeParameterValueNumber(instr, funcIR, var) and
|
||||
result = TInitializeParameterValueNumber(funcIR, var)
|
||||
) or
|
||||
(
|
||||
initializeThisValueNumber(instr, funcIR) and
|
||||
result = TInitializeThisValueNumber(funcIR)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
constantValueNumber(instr, funcIR, type, value) and
|
||||
result = TConstantValueNumber(funcIR, type, value)
|
||||
) or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, funcIR, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(funcIR, field, objectAddress)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
unaryValueNumber(instr, funcIR, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(funcIR, opcode, type, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
inheritanceConversionValueNumber(instr, funcIR, opcode, baseClass, derivedClass,
|
||||
operand) and
|
||||
result = TInheritanceConversionValueNumber(funcIR, opcode, baseClass, derivedClass, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand)
|
||||
) or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR
|
||||
@@ -30,7 +30,9 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
cached newtype TInstructionTag =
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) or
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
}
|
||||
@@ -195,6 +197,10 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached Expr getInstructionResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getResultExpression()
|
||||
}
|
||||
|
||||
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
}
|
||||
|
||||
@@ -5,3 +5,30 @@ import IRVariable
|
||||
import OperandTag
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* Class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
string toString() {
|
||||
result = "IRPropertyProvider"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,14 @@ module InstructionSanity {
|
||||
operand = op.getOperand(tag) and
|
||||
operand.getFunctionIR() != op.getFunctionIR()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,6 +309,13 @@ class Instruction extends Construction::TInstruction {
|
||||
result = ast.getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Expr` whose results is computed by this instruction, if any.
|
||||
*/
|
||||
final Expr getResultExpression() {
|
||||
result = Construction::getInstructionResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -554,6 +569,15 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes the `this` pointer parameter of the enclosing function.
|
||||
*/
|
||||
class InitializeThisInstruction extends Instruction {
|
||||
InitializeThisInstruction() {
|
||||
opcode instanceof Opcode::InitializeThis
|
||||
}
|
||||
}
|
||||
|
||||
class FieldAddressInstruction extends FieldInstruction {
|
||||
FieldAddressInstruction() {
|
||||
opcode instanceof Opcode::FieldAddress
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
private import IR
|
||||
import cpp
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getInstructionProperty(instr, key)
|
||||
)
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getBlockProperty(block, key)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableFunctionIR(FunctionIR funcIR) or
|
||||
TPrintableIRBlock(IRBlock block) or
|
||||
@@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
result.getFunctionIR() = block.getFunctionIR()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() {
|
||||
result = block
|
||||
}
|
||||
@@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
import cpp
|
||||
private import IR
|
||||
|
||||
/**
|
||||
* Provides additional information about value numbering in IR dumps.
|
||||
*/
|
||||
class ValueNumberPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
exists(ValueNumber vn |
|
||||
vn = valueNumber(instr) and
|
||||
key = "valnum" and
|
||||
if strictcount(vn.getAnInstruction()) > 1 then
|
||||
result = vn.toString()
|
||||
else
|
||||
result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
variableAddressValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
initializeParameterValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(FunctionIR funcIR) {
|
||||
initializeThisValueNumber(_, funcIR)
|
||||
} or
|
||||
TConstantValueNumber(FunctionIR funcIR, Type type, string value) {
|
||||
constantValueNumber(_, funcIR, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, funcIR, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) {
|
||||
unaryValueNumber(_, funcIR, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(FunctionIR funcIR, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand) {
|
||||
inheritanceConversionValueNumber(_, funcIR, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
TUniqueValueNumber(FunctionIR funcIR, Instruction instr) {
|
||||
uniqueValueNumber(instr, funcIR)
|
||||
}
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() {
|
||||
result = getExampleInstruction().getResultId()
|
||||
}
|
||||
|
||||
final Location getLocation() {
|
||||
result = getExampleInstruction().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
* least one result.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
this = valueNumber(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result = min(Instruction instr |
|
||||
instr = getAnInstruction() |
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
* For example:
|
||||
* ```
|
||||
* Point p = { 1, 2 };
|
||||
* Point q = p;
|
||||
* int a = p.x;
|
||||
* ```
|
||||
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
|
||||
* definition because it accesses the exact same memory.
|
||||
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
|
||||
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
|
||||
*
|
||||
* This concept should probably be exposed in the public IR API.
|
||||
*/
|
||||
private class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
exists(Instruction def |
|
||||
def = this.getSourceValue() and
|
||||
(
|
||||
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
|
||||
not def.hasMemoryResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this library knows how to assign a value number to the specified instruction, other than
|
||||
* a `unique` value number that is never shared by multiple instructions.
|
||||
*/
|
||||
private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof VariableAddressInstruction or
|
||||
instr instanceof InitializeParameterInstruction or
|
||||
instr instanceof InitializeThisInstruction or
|
||||
instr instanceof ConstantInstruction or
|
||||
instr instanceof FieldAddressInstruction or
|
||||
instr instanceof BinaryInstruction or
|
||||
instr instanceof UnaryInstruction or
|
||||
instr instanceof PointerArithmeticInstruction or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR,
|
||||
IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(InitializeParameterInstruction instr,
|
||||
FunctionIR funcIR, IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type,
|
||||
string value) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR,
|
||||
Field field, ValueNumber objectAddress) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getField() = field and
|
||||
valueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof PointerArithmeticInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof InheritanceConversionInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(InheritanceConversionInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` should be assigned a unique value number because this library does not know how
|
||||
* to determine if two instances of that instruction are equivalent.
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr.getResultType() instanceof VoidType) and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr) or
|
||||
exists(FunctionIR funcIR |
|
||||
uniqueValueNumber(instr, funcIR) and
|
||||
result = TUniqueValueNumber(funcIR, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(FunctionIR funcIR |
|
||||
funcIR = instr.getFunctionIR() and
|
||||
(
|
||||
exists(IRVariable var |
|
||||
variableAddressValueNumber(instr, funcIR, var) and
|
||||
result = TVariableAddressValueNumber(funcIR, var)
|
||||
) or
|
||||
exists(IRVariable var |
|
||||
initializeParameterValueNumber(instr, funcIR, var) and
|
||||
result = TInitializeParameterValueNumber(funcIR, var)
|
||||
) or
|
||||
(
|
||||
initializeThisValueNumber(instr, funcIR) and
|
||||
result = TInitializeThisValueNumber(funcIR)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
constantValueNumber(instr, funcIR, type, value) and
|
||||
result = TConstantValueNumber(funcIR, type, value)
|
||||
) or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, funcIR, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(funcIR, field, objectAddress)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
unaryValueNumber(instr, funcIR, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(funcIR, opcode, type, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
inheritanceConversionValueNumber(instr, funcIR, opcode, baseClass, derivedClass,
|
||||
operand) and
|
||||
result = TInheritanceConversionValueNumber(funcIR, opcode, baseClass, derivedClass, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand)
|
||||
) or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
@@ -4,6 +4,7 @@ import IRBlockConstruction as BlockConstruction
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
|
||||
class InstructionTagType extends TInstructionTag {
|
||||
@@ -73,6 +74,13 @@ cached private module Cached {
|
||||
none()
|
||||
}
|
||||
|
||||
cached Expr getInstructionResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
translatedExpr = getTranslatedExpr(result) and
|
||||
instruction = translatedExpr.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
cached Instruction getInstructionOperand(Instruction instruction, OperandTag tag) {
|
||||
result = getInstructionTranslatedElement(instruction).getInstructionOperand(
|
||||
instruction.getTag(), tag)
|
||||
|
||||
@@ -5,3 +5,30 @@ import IRVariable
|
||||
import OperandTag
|
||||
import semmle.code.cpp.ir.implementation.EdgeKind
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind
|
||||
|
||||
private newtype TIRPropertyProvider = MkIRPropertyProvider()
|
||||
|
||||
/**
|
||||
* Class that provides additional properties to be dumped for IR instructions and blocks when using
|
||||
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
|
||||
* single instance of this class to specify the additional properties computed by the library.
|
||||
*/
|
||||
class IRPropertyProvider extends TIRPropertyProvider {
|
||||
string toString() {
|
||||
result = "IRPropertyProvider"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified instruction.
|
||||
*/
|
||||
string getInstructionProperty(Instruction instruction, string key) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the property named `key` for the specified block.
|
||||
*/
|
||||
string getBlockProperty(IRBlock block, string key) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,14 @@ module InstructionSanity {
|
||||
operand = op.getOperand(tag) and
|
||||
operand.getFunctionIR() != op.getFunctionIR()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,6 +309,13 @@ class Instruction extends Construction::TInstruction {
|
||||
result = ast.getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Expr` whose results is computed by this instruction, if any.
|
||||
*/
|
||||
final Expr getResultExpression() {
|
||||
result = Construction::getInstructionResultExpression(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
* instruction does not produce a result, its result type will be `VoidType`.
|
||||
@@ -554,6 +569,15 @@ class InitializeParameterInstruction extends VariableInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that initializes the `this` pointer parameter of the enclosing function.
|
||||
*/
|
||||
class InitializeThisInstruction extends Instruction {
|
||||
InitializeThisInstruction() {
|
||||
opcode instanceof Opcode::InitializeThis
|
||||
}
|
||||
}
|
||||
|
||||
class FieldAddressInstruction extends FieldInstruction {
|
||||
FieldAddressInstruction() {
|
||||
opcode instanceof Opcode::FieldAddress
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
private import IR
|
||||
import cpp
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getInstructionProperty(instr, key)
|
||||
)
|
||||
}
|
||||
|
||||
private string getAdditionalBlockProperty(IRBlock block, string key) {
|
||||
exists(IRPropertyProvider provider |
|
||||
result = provider.getBlockProperty(block, key)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TPrintableIRNode =
|
||||
TPrintableFunctionIR(FunctionIR funcIR) or
|
||||
TPrintableIRBlock(IRBlock block) or
|
||||
@@ -135,6 +147,11 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
|
||||
result.getFunctionIR() = block.getFunctionIR()
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalBlockProperty(block, key)
|
||||
}
|
||||
|
||||
final IRBlock getBlock() {
|
||||
result = block
|
||||
}
|
||||
@@ -185,6 +202,11 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
||||
final Instruction getInstruction() {
|
||||
result = instr
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = PrintableIRNode.super.getProperty(key) or
|
||||
result = getAdditionalInstructionProperty(instr, key)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) {
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
private import internal.ValueNumberingInternal
|
||||
import cpp
|
||||
private import IR
|
||||
|
||||
/**
|
||||
* Provides additional information about value numbering in IR dumps.
|
||||
*/
|
||||
class ValueNumberPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instr, string key) {
|
||||
exists(ValueNumber vn |
|
||||
vn = valueNumber(instr) and
|
||||
key = "valnum" and
|
||||
if strictcount(vn.getAnInstruction()) > 1 then
|
||||
result = vn.toString()
|
||||
else
|
||||
result = "unique"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
variableAddressValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeParameterValueNumber(FunctionIR funcIR, IRVariable var) {
|
||||
initializeParameterValueNumber(_, funcIR, var)
|
||||
} or
|
||||
TInitializeThisValueNumber(FunctionIR funcIR) {
|
||||
initializeThisValueNumber(_, funcIR)
|
||||
} or
|
||||
TConstantValueNumber(FunctionIR funcIR, Type type, string value) {
|
||||
constantValueNumber(_, funcIR, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(FunctionIR funcIR, Field field, ValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, funcIR, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
binaryValueNumber(_, funcIR, opcode, type, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(FunctionIR funcIR, Opcode opcode, Type type, int elementSize,
|
||||
ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
pointerArithmeticValueNumber(_, funcIR, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(FunctionIR funcIR, Opcode opcode, Type type, ValueNumber operand) {
|
||||
unaryValueNumber(_, funcIR, opcode, type, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(FunctionIR funcIR, Opcode opcode, Class baseClass,
|
||||
Class derivedClass, ValueNumber operand) {
|
||||
inheritanceConversionValueNumber(_, funcIR, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
TUniqueValueNumber(FunctionIR funcIR, Instruction instr) {
|
||||
uniqueValueNumber(instr, funcIR)
|
||||
}
|
||||
|
||||
/**
|
||||
* The value number assigned to a particular set of instructions that produce equivalent results.
|
||||
*/
|
||||
class ValueNumber extends TValueNumber {
|
||||
final string toString() {
|
||||
result = getExampleInstruction().getResultId()
|
||||
}
|
||||
|
||||
final Location getLocation() {
|
||||
result = getExampleInstruction().getLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instructions that have been assigned this value number. This will always produce at
|
||||
* least one result.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
this = valueNumber(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets one of the instructions that was assigned this value number. The chosen instuction is
|
||||
* deterministic but arbitrary. Intended for use only in debugging.
|
||||
*/
|
||||
final Instruction getExampleInstruction() {
|
||||
result = min(Instruction instr |
|
||||
instr = getAnInstruction() |
|
||||
instr order by instr.getBlock().getDisplayIndex(), instr.getDisplayIndexInBlock()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
|
||||
* operand.
|
||||
* For example:
|
||||
* ```
|
||||
* Point p = { 1, 2 };
|
||||
* Point q = p;
|
||||
* int a = p.x;
|
||||
* ```
|
||||
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
|
||||
* definition because it accesses the exact same memory.
|
||||
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
|
||||
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
|
||||
*
|
||||
* This concept should probably be exposed in the public IR API.
|
||||
*/
|
||||
private class CongruentCopyInstruction extends CopyInstruction {
|
||||
CongruentCopyInstruction() {
|
||||
exists(Instruction def |
|
||||
def = this.getSourceValue() and
|
||||
(
|
||||
def.getResultMemoryAccess() instanceof IndirectMemoryAccess or
|
||||
not def.hasMemoryResult()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this library knows how to assign a value number to the specified instruction, other than
|
||||
* a `unique` value number that is never shared by multiple instructions.
|
||||
*/
|
||||
private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof VariableAddressInstruction or
|
||||
instr instanceof InitializeParameterInstruction or
|
||||
instr instanceof InitializeThisInstruction or
|
||||
instr instanceof ConstantInstruction or
|
||||
instr instanceof FieldAddressInstruction or
|
||||
instr instanceof BinaryInstruction or
|
||||
instr instanceof UnaryInstruction or
|
||||
instr instanceof PointerArithmeticInstruction or
|
||||
instr instanceof CongruentCopyInstruction
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(VariableAddressInstruction instr, FunctionIR funcIR,
|
||||
IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(InitializeParameterInstruction instr,
|
||||
FunctionIR funcIR, IRVariable var) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getVariable() = var
|
||||
}
|
||||
|
||||
private predicate initializeThisValueNumber(InitializeThisInstruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(ConstantInstruction instr, FunctionIR funcIR, Type type,
|
||||
string value) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getResultType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
private predicate fieldAddressValueNumber(FieldAddressInstruction instr, FunctionIR funcIR,
|
||||
Field field, ValueNumber objectAddress) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getField() = field and
|
||||
valueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(BinaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber leftOperand, ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof PointerArithmeticInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(PointerArithmeticInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
valueNumber(instr.getLeftOperand()) = leftOperand and
|
||||
valueNumber(instr.getRightOperand()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(UnaryInstruction instr, FunctionIR funcIR, Opcode opcode,
|
||||
Type type, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr instanceof InheritanceConversionInstruction) and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultType() = type and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
private predicate inheritanceConversionValueNumber(InheritanceConversionInstruction instr,
|
||||
FunctionIR funcIR, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
valueNumber(instr.getOperand()) = operand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` should be assigned a unique value number because this library does not know how
|
||||
* to determine if two instances of that instruction are equivalent.
|
||||
*/
|
||||
private predicate uniqueValueNumber(Instruction instr, FunctionIR funcIR) {
|
||||
instr.getFunctionIR() = funcIR and
|
||||
(not instr.getResultType() instanceof VoidType) and
|
||||
not numberableInstruction(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any. Returns at most one result.
|
||||
*/
|
||||
ValueNumber valueNumber(Instruction instr) {
|
||||
result = nonUniqueValueNumber(instr) or
|
||||
exists(FunctionIR funcIR |
|
||||
uniqueValueNumber(instr, funcIR) and
|
||||
result = TUniqueValueNumber(funcIR, instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
|
||||
* value number.
|
||||
*/
|
||||
private ValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
exists(FunctionIR funcIR |
|
||||
funcIR = instr.getFunctionIR() and
|
||||
(
|
||||
exists(IRVariable var |
|
||||
variableAddressValueNumber(instr, funcIR, var) and
|
||||
result = TVariableAddressValueNumber(funcIR, var)
|
||||
) or
|
||||
exists(IRVariable var |
|
||||
initializeParameterValueNumber(instr, funcIR, var) and
|
||||
result = TInitializeParameterValueNumber(funcIR, var)
|
||||
) or
|
||||
(
|
||||
initializeThisValueNumber(instr, funcIR) and
|
||||
result = TInitializeThisValueNumber(funcIR)
|
||||
) or
|
||||
exists(Type type, string value |
|
||||
constantValueNumber(instr, funcIR, type, value) and
|
||||
result = TConstantValueNumber(funcIR, type, value)
|
||||
) or
|
||||
exists(Field field, ValueNumber objectAddress |
|
||||
fieldAddressValueNumber(instr, funcIR, field, objectAddress) and
|
||||
result = TFieldAddressValueNumber(funcIR, field, objectAddress)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
|
||||
binaryValueNumber(instr, funcIR, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(funcIR, opcode, type, leftOperand, rightOperand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, ValueNumber operand |
|
||||
unaryValueNumber(instr, funcIR, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(funcIR, opcode, type, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
|
||||
inheritanceConversionValueNumber(instr, funcIR, opcode, baseClass, derivedClass,
|
||||
operand) and
|
||||
result = TInheritanceConversionValueNumber(funcIR, opcode, baseClass, derivedClass, operand)
|
||||
) or
|
||||
exists(Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
|
||||
ValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
result = TPointerArithmeticValueNumber(funcIR, opcode, type, elementSize, leftOperand,
|
||||
rightOperand)
|
||||
) or
|
||||
// The value number of a copy is just the value number of its source value.
|
||||
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
@@ -30,7 +30,9 @@ cached private module Cached {
|
||||
}
|
||||
|
||||
cached newtype TInstructionTag =
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) or
|
||||
WrappedInstructionTag(OldIR::Instruction oldInstruction) {
|
||||
not oldInstruction instanceof OldIR::PhiInstruction
|
||||
} or
|
||||
PhiTag(Alias::VirtualVariable vvar, OldIR::IRBlock block) {
|
||||
hasPhiNode(vvar, block)
|
||||
}
|
||||
@@ -195,6 +197,10 @@ cached private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached Expr getInstructionResultExpression(Instruction instruction) {
|
||||
result = getOldInstruction(instruction).getResultExpression()
|
||||
}
|
||||
|
||||
cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
|
||||
result = getNewInstruction(getOldInstruction(instruction).getSuccessor(kind))
|
||||
}
|
||||
|
||||
1095
cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll
Normal file
1095
cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,3 +5,4 @@ missingPhiOperand
|
||||
instructionWithoutSuccessor
|
||||
unnecessaryPhiInstruction
|
||||
operandAcrossFunctions
|
||||
instructionWithoutUniqueBlock
|
||||
|
||||
@@ -5,3 +5,4 @@ missingPhiOperand
|
||||
instructionWithoutSuccessor
|
||||
unnecessaryPhiInstruction
|
||||
operandAcrossFunctions
|
||||
instructionWithoutUniqueBlock
|
||||
|
||||
@@ -5,3 +5,4 @@ missingPhiOperand
|
||||
instructionWithoutSuccessor
|
||||
unnecessaryPhiInstruction
|
||||
operandAcrossFunctions
|
||||
instructionWithoutUniqueBlock
|
||||
|
||||
@@ -27,3 +27,6 @@
|
||||
| test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 |
|
||||
| test.cpp:77:20:77:30 | (signed short)... | 77:c20-c30 79:c7-c7 |
|
||||
| test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 |
|
||||
| test.cpp:105:11:105:12 | (Base *)... | 105:c11-c12 106:c14-c35 107:c11-c12 |
|
||||
| test.cpp:105:11:105:12 | pd | 105:c11-c12 106:c33-c34 |
|
||||
| test.cpp:105:15:105:15 | b | 105:c15-c15 107:c15-c15 109:c10-c10 |
|
||||
|
||||
@@ -0,0 +1,714 @@
|
||||
test.cpp:
|
||||
# 1| test00(int, int) -> int
|
||||
# 1| Block 0
|
||||
# 1| v0_0(void) = EnterFunction :
|
||||
# 1| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 1| valnum = unique
|
||||
# 1| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 1| valnum = r0_2
|
||||
# 1| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 1| valnum = m0_3
|
||||
# 1| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 1| valnum = r0_4
|
||||
# 1| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 1| valnum = m0_5
|
||||
# 2| r0_6(glval<int>) = VariableAddress[x] :
|
||||
# 2| valnum = r0_6
|
||||
# 2| m0_7(int) = Uninitialized : r0_6
|
||||
# 2| valnum = unique
|
||||
# 2| r0_8(glval<int>) = VariableAddress[y] :
|
||||
# 2| valnum = r0_8
|
||||
# 2| m0_9(int) = Uninitialized : r0_8
|
||||
# 2| valnum = unique
|
||||
# 3| r0_10(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 3| valnum = unique
|
||||
# 3| m0_11(unsigned char) = Uninitialized : r0_10
|
||||
# 3| valnum = unique
|
||||
# 5| r0_12(glval<int>) = VariableAddress[p0] :
|
||||
# 5| valnum = r0_2
|
||||
# 5| r0_13(int) = Load : r0_12, m0_3
|
||||
# 5| valnum = m0_3
|
||||
# 5| r0_14(glval<int>) = VariableAddress[p1] :
|
||||
# 5| valnum = r0_4
|
||||
# 5| r0_15(int) = Load : r0_14, m0_5
|
||||
# 5| valnum = m0_5
|
||||
# 5| r0_16(int) = Add : r0_13, r0_15
|
||||
# 5| valnum = r0_16
|
||||
# 5| r0_17(glval<int>) = VariableAddress[x] :
|
||||
# 5| valnum = r0_6
|
||||
# 5| m0_18(int) = Store : r0_17, r0_16
|
||||
# 5| valnum = r0_16
|
||||
# 6| r0_19(glval<int>) = VariableAddress[p0] :
|
||||
# 6| valnum = r0_2
|
||||
# 6| r0_20(int) = Load : r0_19, m0_3
|
||||
# 6| valnum = m0_3
|
||||
# 6| r0_21(glval<int>) = VariableAddress[p1] :
|
||||
# 6| valnum = r0_4
|
||||
# 6| r0_22(int) = Load : r0_21, m0_5
|
||||
# 6| valnum = m0_5
|
||||
# 6| r0_23(int) = Add : r0_20, r0_22
|
||||
# 6| valnum = r0_16
|
||||
# 6| r0_24(glval<int>) = VariableAddress[x] :
|
||||
# 6| valnum = r0_6
|
||||
# 6| m0_25(int) = Store : r0_24, r0_23
|
||||
# 6| valnum = r0_16
|
||||
# 7| r0_26(glval<int>) = VariableAddress[x] :
|
||||
# 7| valnum = r0_6
|
||||
# 7| r0_27(int) = Load : r0_26, m0_25
|
||||
# 7| valnum = r0_16
|
||||
# 7| r0_28(glval<int>) = VariableAddress[y] :
|
||||
# 7| valnum = r0_8
|
||||
# 7| m0_29(int) = Store : r0_28, r0_27
|
||||
# 7| valnum = r0_16
|
||||
# 8| v0_30(void) = NoOp :
|
||||
# 1| r0_31(glval<int>) = VariableAddress[#return] :
|
||||
# 1| valnum = unique
|
||||
# 1| v0_32(void) = ReturnValue : r0_31
|
||||
# 1| v0_33(void) = UnmodeledUse : mu*
|
||||
# 1| v0_34(void) = ExitFunction :
|
||||
|
||||
# 12| test01(int, int) -> int
|
||||
# 12| Block 0
|
||||
# 12| v0_0(void) = EnterFunction :
|
||||
# 12| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 12| valnum = unique
|
||||
# 12| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 12| valnum = r0_2
|
||||
# 12| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 12| valnum = m0_3
|
||||
# 12| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 12| valnum = r0_4
|
||||
# 12| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 12| valnum = m0_5
|
||||
# 13| r0_6(glval<int>) = VariableAddress[x] :
|
||||
# 13| valnum = r0_6
|
||||
# 13| m0_7(int) = Uninitialized : r0_6
|
||||
# 13| valnum = unique
|
||||
# 13| r0_8(glval<int>) = VariableAddress[y] :
|
||||
# 13| valnum = r0_8
|
||||
# 13| m0_9(int) = Uninitialized : r0_8
|
||||
# 13| valnum = unique
|
||||
# 14| r0_10(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 14| valnum = unique
|
||||
# 14| m0_11(unsigned char) = Uninitialized : r0_10
|
||||
# 14| valnum = unique
|
||||
# 16| r0_12(glval<int>) = VariableAddress[p0] :
|
||||
# 16| valnum = r0_2
|
||||
# 16| r0_13(int) = Load : r0_12, m0_3
|
||||
# 16| valnum = m0_3
|
||||
# 16| r0_14(glval<int>) = VariableAddress[p1] :
|
||||
# 16| valnum = r0_4
|
||||
# 16| r0_15(int) = Load : r0_14, m0_5
|
||||
# 16| valnum = m0_5
|
||||
# 16| r0_16(int) = Add : r0_13, r0_15
|
||||
# 16| valnum = r0_16
|
||||
# 16| r0_17(glval<int>) = VariableAddress[global01] :
|
||||
# 16| valnum = r0_17
|
||||
# 16| r0_18(int) = Load : r0_17, mu0_1
|
||||
# 16| valnum = unique
|
||||
# 16| r0_19(int) = Add : r0_16, r0_18
|
||||
# 16| valnum = r0_19
|
||||
# 16| r0_20(glval<int>) = VariableAddress[x] :
|
||||
# 16| valnum = r0_6
|
||||
# 16| m0_21(int) = Store : r0_20, r0_19
|
||||
# 16| valnum = r0_19
|
||||
# 17| r0_22(glval<int>) = VariableAddress[p0] :
|
||||
# 17| valnum = r0_2
|
||||
# 17| r0_23(int) = Load : r0_22, m0_3
|
||||
# 17| valnum = m0_3
|
||||
# 17| r0_24(glval<int>) = VariableAddress[p1] :
|
||||
# 17| valnum = r0_4
|
||||
# 17| r0_25(int) = Load : r0_24, m0_5
|
||||
# 17| valnum = m0_5
|
||||
# 17| r0_26(int) = Add : r0_23, r0_25
|
||||
# 17| valnum = r0_16
|
||||
# 17| r0_27(glval<int>) = VariableAddress[global01] :
|
||||
# 17| valnum = r0_17
|
||||
# 17| r0_28(int) = Load : r0_27, mu0_1
|
||||
# 17| valnum = unique
|
||||
# 17| r0_29(int) = Add : r0_26, r0_28
|
||||
# 17| valnum = r0_29
|
||||
# 17| r0_30(glval<int>) = VariableAddress[x] :
|
||||
# 17| valnum = r0_6
|
||||
# 17| m0_31(int) = Store : r0_30, r0_29
|
||||
# 17| valnum = r0_29
|
||||
# 18| r0_32(glval<int>) = VariableAddress[x] :
|
||||
# 18| valnum = r0_6
|
||||
# 18| r0_33(int) = Load : r0_32, m0_31
|
||||
# 18| valnum = r0_29
|
||||
# 18| r0_34(glval<int>) = VariableAddress[y] :
|
||||
# 18| valnum = r0_8
|
||||
# 18| m0_35(int) = Store : r0_34, r0_33
|
||||
# 18| valnum = r0_29
|
||||
# 19| v0_36(void) = NoOp :
|
||||
# 12| r0_37(glval<int>) = VariableAddress[#return] :
|
||||
# 12| valnum = unique
|
||||
# 12| v0_38(void) = ReturnValue : r0_37
|
||||
# 12| v0_39(void) = UnmodeledUse : mu*
|
||||
# 12| v0_40(void) = ExitFunction :
|
||||
|
||||
# 25| test02(int, int) -> int
|
||||
# 25| Block 0
|
||||
# 25| v0_0(void) = EnterFunction :
|
||||
# 25| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 25| valnum = unique
|
||||
# 25| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 25| valnum = r0_2
|
||||
# 25| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 25| valnum = m0_3
|
||||
# 25| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 25| valnum = r0_4
|
||||
# 25| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 25| valnum = m0_5
|
||||
# 26| r0_6(glval<int>) = VariableAddress[x] :
|
||||
# 26| valnum = r0_6
|
||||
# 26| m0_7(int) = Uninitialized : r0_6
|
||||
# 26| valnum = unique
|
||||
# 26| r0_8(glval<int>) = VariableAddress[y] :
|
||||
# 26| valnum = r0_8
|
||||
# 26| m0_9(int) = Uninitialized : r0_8
|
||||
# 26| valnum = unique
|
||||
# 27| r0_10(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 27| valnum = unique
|
||||
# 27| m0_11(unsigned char) = Uninitialized : r0_10
|
||||
# 27| valnum = unique
|
||||
# 29| r0_12(glval<int>) = VariableAddress[p0] :
|
||||
# 29| valnum = r0_2
|
||||
# 29| r0_13(int) = Load : r0_12, m0_3
|
||||
# 29| valnum = m0_3
|
||||
# 29| r0_14(glval<int>) = VariableAddress[p1] :
|
||||
# 29| valnum = r0_4
|
||||
# 29| r0_15(int) = Load : r0_14, m0_5
|
||||
# 29| valnum = m0_5
|
||||
# 29| r0_16(int) = Add : r0_13, r0_15
|
||||
# 29| valnum = r0_16
|
||||
# 29| r0_17(glval<int>) = VariableAddress[global02] :
|
||||
# 29| valnum = r0_17
|
||||
# 29| r0_18(int) = Load : r0_17, mu0_1
|
||||
# 29| valnum = unique
|
||||
# 29| r0_19(int) = Add : r0_16, r0_18
|
||||
# 29| valnum = r0_19
|
||||
# 29| r0_20(glval<int>) = VariableAddress[x] :
|
||||
# 29| valnum = r0_6
|
||||
# 29| m0_21(int) = Store : r0_20, r0_19
|
||||
# 29| valnum = r0_19
|
||||
# 30| r0_22(glval<unknown>) = FunctionAddress[change_global02] :
|
||||
# 30| valnum = unique
|
||||
# 30| v0_23(void) = Call : r0_22
|
||||
# 31| r0_24(glval<int>) = VariableAddress[p0] :
|
||||
# 31| valnum = r0_2
|
||||
# 31| r0_25(int) = Load : r0_24, m0_3
|
||||
# 31| valnum = m0_3
|
||||
# 31| r0_26(glval<int>) = VariableAddress[p1] :
|
||||
# 31| valnum = r0_4
|
||||
# 31| r0_27(int) = Load : r0_26, m0_5
|
||||
# 31| valnum = m0_5
|
||||
# 31| r0_28(int) = Add : r0_25, r0_27
|
||||
# 31| valnum = r0_16
|
||||
# 31| r0_29(glval<int>) = VariableAddress[global02] :
|
||||
# 31| valnum = r0_17
|
||||
# 31| r0_30(int) = Load : r0_29, mu0_1
|
||||
# 31| valnum = unique
|
||||
# 31| r0_31(int) = Add : r0_28, r0_30
|
||||
# 31| valnum = r0_31
|
||||
# 31| r0_32(glval<int>) = VariableAddress[x] :
|
||||
# 31| valnum = r0_6
|
||||
# 31| m0_33(int) = Store : r0_32, r0_31
|
||||
# 31| valnum = r0_31
|
||||
# 32| r0_34(glval<int>) = VariableAddress[x] :
|
||||
# 32| valnum = r0_6
|
||||
# 32| r0_35(int) = Load : r0_34, m0_33
|
||||
# 32| valnum = r0_31
|
||||
# 32| r0_36(glval<int>) = VariableAddress[y] :
|
||||
# 32| valnum = r0_8
|
||||
# 32| m0_37(int) = Store : r0_36, r0_35
|
||||
# 32| valnum = r0_31
|
||||
# 33| v0_38(void) = NoOp :
|
||||
# 25| r0_39(glval<int>) = VariableAddress[#return] :
|
||||
# 25| valnum = unique
|
||||
# 25| v0_40(void) = ReturnValue : r0_39
|
||||
# 25| v0_41(void) = UnmodeledUse : mu*
|
||||
# 25| v0_42(void) = ExitFunction :
|
||||
|
||||
# 39| test03(int, int, int *) -> int
|
||||
# 39| Block 0
|
||||
# 39| v0_0(void) = EnterFunction :
|
||||
# 39| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 39| valnum = unique
|
||||
# 39| r0_2(glval<int>) = VariableAddress[p0] :
|
||||
# 39| valnum = r0_2
|
||||
# 39| m0_3(int) = InitializeParameter[p0] : r0_2
|
||||
# 39| valnum = m0_3
|
||||
# 39| r0_4(glval<int>) = VariableAddress[p1] :
|
||||
# 39| valnum = r0_4
|
||||
# 39| m0_5(int) = InitializeParameter[p1] : r0_4
|
||||
# 39| valnum = m0_5
|
||||
# 39| r0_6(glval<int *>) = VariableAddress[p2] :
|
||||
# 39| valnum = r0_6
|
||||
# 39| m0_7(int *) = InitializeParameter[p2] : r0_6
|
||||
# 39| valnum = m0_7
|
||||
# 40| r0_8(glval<int>) = VariableAddress[x] :
|
||||
# 40| valnum = r0_8
|
||||
# 40| m0_9(int) = Uninitialized : r0_8
|
||||
# 40| valnum = unique
|
||||
# 40| r0_10(glval<int>) = VariableAddress[y] :
|
||||
# 40| valnum = r0_10
|
||||
# 40| m0_11(int) = Uninitialized : r0_10
|
||||
# 40| valnum = unique
|
||||
# 41| r0_12(glval<unsigned char>) = VariableAddress[b] :
|
||||
# 41| valnum = unique
|
||||
# 41| m0_13(unsigned char) = Uninitialized : r0_12
|
||||
# 41| valnum = unique
|
||||
# 43| r0_14(glval<int>) = VariableAddress[p0] :
|
||||
# 43| valnum = r0_2
|
||||
# 43| r0_15(int) = Load : r0_14, m0_3
|
||||
# 43| valnum = m0_3
|
||||
# 43| r0_16(glval<int>) = VariableAddress[p1] :
|
||||
# 43| valnum = r0_4
|
||||
# 43| r0_17(int) = Load : r0_16, m0_5
|
||||
# 43| valnum = m0_5
|
||||
# 43| r0_18(int) = Add : r0_15, r0_17
|
||||
# 43| valnum = r0_18
|
||||
# 43| r0_19(glval<int>) = VariableAddress[global03] :
|
||||
# 43| valnum = r0_19
|
||||
# 43| r0_20(int) = Load : r0_19, mu0_1
|
||||
# 43| valnum = unique
|
||||
# 43| r0_21(int) = Add : r0_18, r0_20
|
||||
# 43| valnum = r0_21
|
||||
# 43| r0_22(glval<int>) = VariableAddress[x] :
|
||||
# 43| valnum = r0_8
|
||||
# 43| m0_23(int) = Store : r0_22, r0_21
|
||||
# 43| valnum = r0_21
|
||||
# 44| r0_24(int) = Constant[0] :
|
||||
# 44| valnum = r0_24
|
||||
# 44| r0_25(glval<int *>) = VariableAddress[p2] :
|
||||
# 44| valnum = r0_6
|
||||
# 44| r0_26(int *) = Load : r0_25, m0_7
|
||||
# 44| valnum = m0_7
|
||||
# 44| mu0_27(int) = Store : r0_26, r0_24
|
||||
# 44| valnum = r0_24
|
||||
# 45| r0_28(glval<int>) = VariableAddress[p0] :
|
||||
# 45| valnum = r0_2
|
||||
# 45| r0_29(int) = Load : r0_28, m0_3
|
||||
# 45| valnum = m0_3
|
||||
# 45| r0_30(glval<int>) = VariableAddress[p1] :
|
||||
# 45| valnum = r0_4
|
||||
# 45| r0_31(int) = Load : r0_30, m0_5
|
||||
# 45| valnum = m0_5
|
||||
# 45| r0_32(int) = Add : r0_29, r0_31
|
||||
# 45| valnum = r0_18
|
||||
# 45| r0_33(glval<int>) = VariableAddress[global03] :
|
||||
# 45| valnum = r0_19
|
||||
# 45| r0_34(int) = Load : r0_33, mu0_1
|
||||
# 45| valnum = unique
|
||||
# 45| r0_35(int) = Add : r0_32, r0_34
|
||||
# 45| valnum = r0_35
|
||||
# 45| r0_36(glval<int>) = VariableAddress[x] :
|
||||
# 45| valnum = r0_8
|
||||
# 45| m0_37(int) = Store : r0_36, r0_35
|
||||
# 45| valnum = r0_35
|
||||
# 46| r0_38(glval<int>) = VariableAddress[x] :
|
||||
# 46| valnum = r0_8
|
||||
# 46| r0_39(int) = Load : r0_38, m0_37
|
||||
# 46| valnum = r0_35
|
||||
# 46| r0_40(glval<int>) = VariableAddress[y] :
|
||||
# 46| valnum = r0_10
|
||||
# 46| m0_41(int) = Store : r0_40, r0_39
|
||||
# 46| valnum = r0_35
|
||||
# 47| v0_42(void) = NoOp :
|
||||
# 39| r0_43(glval<int>) = VariableAddress[#return] :
|
||||
# 39| valnum = unique
|
||||
# 39| v0_44(void) = ReturnValue : r0_43
|
||||
# 39| v0_45(void) = UnmodeledUse : mu*
|
||||
# 39| v0_46(void) = ExitFunction :
|
||||
|
||||
# 49| my_strspn(const char *, const char *) -> unsigned int
|
||||
# 49| Block 0
|
||||
# 49| v0_0(void) = EnterFunction :
|
||||
# 49| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 49| valnum = unique
|
||||
# 49| r0_2(glval<char *>) = VariableAddress[str] :
|
||||
# 49| valnum = r0_2
|
||||
# 49| m0_3(char *) = InitializeParameter[str] : r0_2
|
||||
# 49| valnum = m0_3
|
||||
# 49| r0_4(glval<char *>) = VariableAddress[chars] :
|
||||
# 49| valnum = r0_4
|
||||
# 49| m0_5(char *) = InitializeParameter[chars] : r0_4
|
||||
# 49| valnum = m0_5
|
||||
# 50| r0_6(glval<char *>) = VariableAddress[ptr] :
|
||||
# 50| valnum = r0_6
|
||||
# 50| m0_7(char *) = Uninitialized : r0_6
|
||||
# 50| valnum = unique
|
||||
# 51| r0_8(glval<unsigned int>) = VariableAddress[result] :
|
||||
# 51| valnum = r0_8
|
||||
# 51| r0_9(unsigned int) = Constant[0] :
|
||||
# 51| valnum = r0_9
|
||||
# 51| m0_10(unsigned int) = Store : r0_8, r0_9
|
||||
# 51| valnum = r0_9
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 53| Block 1
|
||||
# 53| m1_0(unsigned int) = Phi : from 0:m0_10, from 8:m8_4
|
||||
# 53| valnum = unique
|
||||
# 53| r1_1(glval<char *>) = VariableAddress[str] :
|
||||
# 53| valnum = r0_2
|
||||
# 53| r1_2(char *) = Load : r1_1, m0_3
|
||||
# 53| valnum = m0_3
|
||||
# 53| r1_3(char) = Load : r1_2, mu0_1
|
||||
# 53| valnum = unique
|
||||
# 53| r1_4(int) = Convert : r1_3
|
||||
# 53| valnum = unique
|
||||
# 53| r1_5(int) = Constant[0] :
|
||||
# 53| valnum = r1_5
|
||||
# 53| r1_6(bool) = CompareNE : r1_4, r1_5
|
||||
# 53| valnum = unique
|
||||
# 53| v1_7(void) = ConditionalBranch : r1_6
|
||||
#-----| False -> Block 9
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 55| Block 2
|
||||
# 55| r2_0(glval<char *>) = VariableAddress[chars] :
|
||||
# 55| valnum = r0_4
|
||||
# 55| r2_1(char *) = Load : r2_0, m0_5
|
||||
# 55| valnum = m0_5
|
||||
# 55| r2_2(glval<char *>) = VariableAddress[ptr] :
|
||||
# 55| valnum = r0_6
|
||||
# 55| m2_3(char *) = Store : r2_2, r2_1
|
||||
# 55| valnum = m0_5
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 56| Block 3
|
||||
# 56| m3_0(char *) = Phi : from 2:m2_3, from 5:m5_4
|
||||
# 56| valnum = unique
|
||||
# 56| r3_1(glval<char *>) = VariableAddress[ptr] :
|
||||
# 56| valnum = r0_6
|
||||
# 56| r3_2(char *) = Load : r3_1, m3_0
|
||||
# 56| valnum = unique
|
||||
# 56| r3_3(char) = Load : r3_2, mu0_1
|
||||
# 56| valnum = unique
|
||||
# 56| r3_4(int) = Convert : r3_3
|
||||
# 56| valnum = unique
|
||||
# 56| r3_5(glval<char *>) = VariableAddress[str] :
|
||||
# 56| valnum = r0_2
|
||||
# 56| r3_6(char *) = Load : r3_5, m0_3
|
||||
# 56| valnum = m0_3
|
||||
# 56| r3_7(char) = Load : r3_6, mu0_1
|
||||
# 56| valnum = unique
|
||||
# 56| r3_8(int) = Convert : r3_7
|
||||
# 56| valnum = unique
|
||||
# 56| r3_9(bool) = CompareNE : r3_4, r3_8
|
||||
# 56| valnum = unique
|
||||
# 56| v3_10(void) = ConditionalBranch : r3_9
|
||||
#-----| False -> Block 6
|
||||
#-----| True -> Block 4
|
||||
|
||||
# 56| Block 4
|
||||
# 56| r4_0(glval<char *>) = VariableAddress[ptr] :
|
||||
# 56| valnum = r0_6
|
||||
# 56| r4_1(char *) = Load : r4_0, m3_0
|
||||
# 56| valnum = unique
|
||||
# 56| r4_2(char) = Load : r4_1, mu0_1
|
||||
# 56| valnum = unique
|
||||
# 56| r4_3(int) = Convert : r4_2
|
||||
# 56| valnum = unique
|
||||
# 56| r4_4(int) = Constant[0] :
|
||||
# 56| valnum = r1_5
|
||||
# 56| r4_5(bool) = CompareNE : r4_3, r4_4
|
||||
# 56| valnum = unique
|
||||
# 56| v4_6(void) = ConditionalBranch : r4_5
|
||||
#-----| False -> Block 6
|
||||
#-----| True -> Block 5
|
||||
|
||||
# 56| Block 5
|
||||
# 56| r5_0(glval<char *>) = VariableAddress[ptr] :
|
||||
# 56| valnum = r0_6
|
||||
# 56| r5_1(char *) = Load : r5_0, m3_0
|
||||
# 56| valnum = unique
|
||||
# 56| r5_2(int) = Constant[1] :
|
||||
# 56| valnum = unique
|
||||
# 56| r5_3(char *) = PointerAdd[1] : r5_1, r5_2
|
||||
# 56| valnum = r5_3
|
||||
# 56| m5_4(char *) = Store : r5_0, r5_3
|
||||
# 56| valnum = r5_3
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 59| Block 6
|
||||
# 59| r6_0(glval<char *>) = VariableAddress[ptr] :
|
||||
# 59| valnum = r0_6
|
||||
# 59| r6_1(char *) = Load : r6_0, m3_0
|
||||
# 59| valnum = unique
|
||||
# 59| r6_2(char) = Load : r6_1, mu0_1
|
||||
# 59| valnum = unique
|
||||
# 59| r6_3(int) = Convert : r6_2
|
||||
# 59| valnum = unique
|
||||
# 59| r6_4(int) = Constant[0] :
|
||||
# 59| valnum = r1_5
|
||||
# 59| r6_5(bool) = CompareEQ : r6_3, r6_4
|
||||
# 59| valnum = unique
|
||||
# 59| v6_6(void) = ConditionalBranch : r6_5
|
||||
#-----| False -> Block 8
|
||||
#-----| True -> Block 7
|
||||
|
||||
# 60| Block 7
|
||||
# 60| v7_0(void) = NoOp :
|
||||
#-----| Goto -> Block 9
|
||||
|
||||
# 62| Block 8
|
||||
# 62| r8_0(glval<unsigned int>) = VariableAddress[result] :
|
||||
# 62| valnum = r0_8
|
||||
# 62| r8_1(unsigned int) = Load : r8_0, m1_0
|
||||
# 62| valnum = unique
|
||||
# 62| r8_2(unsigned int) = Constant[1] :
|
||||
# 62| valnum = unique
|
||||
# 62| r8_3(unsigned int) = Add : r8_1, r8_2
|
||||
# 62| valnum = r8_3
|
||||
# 62| m8_4(unsigned int) = Store : r8_0, r8_3
|
||||
# 62| valnum = r8_3
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 63| Block 9
|
||||
# 63| v9_0(void) = NoOp :
|
||||
# 65| r9_1(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 65| valnum = r9_1
|
||||
# 65| r9_2(glval<unsigned int>) = VariableAddress[result] :
|
||||
# 65| valnum = r0_8
|
||||
# 65| r9_3(unsigned int) = Load : r9_2, m1_0
|
||||
# 65| valnum = r9_3
|
||||
# 65| m9_4(unsigned int) = Store : r9_1, r9_3
|
||||
# 65| valnum = r9_3
|
||||
# 49| r9_5(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 49| valnum = r9_1
|
||||
# 49| v9_6(void) = ReturnValue : r9_5, m9_4
|
||||
# 49| v9_7(void) = UnmodeledUse : mu*
|
||||
# 49| v9_8(void) = ExitFunction :
|
||||
|
||||
# 75| test04(two_values *) -> void
|
||||
# 75| Block 0
|
||||
# 75| v0_0(void) = EnterFunction :
|
||||
# 75| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 75| valnum = unique
|
||||
# 75| r0_2(glval<two_values *>) = VariableAddress[vals] :
|
||||
# 75| valnum = r0_2
|
||||
# 75| m0_3(two_values *) = InitializeParameter[vals] : r0_2
|
||||
# 75| valnum = m0_3
|
||||
# 77| r0_4(glval<signed short>) = VariableAddress[v] :
|
||||
# 77| valnum = r0_4
|
||||
# 77| r0_5(glval<unknown>) = FunctionAddress[getAValue] :
|
||||
# 77| valnum = unique
|
||||
# 77| r0_6(int) = Call : r0_5
|
||||
# 77| valnum = unique
|
||||
# 77| r0_7(signed short) = Convert : r0_6
|
||||
# 77| valnum = r0_7
|
||||
# 77| m0_8(signed short) = Store : r0_4, r0_7
|
||||
# 77| valnum = r0_7
|
||||
# 79| r0_9(glval<signed short>) = VariableAddress[v] :
|
||||
# 79| valnum = r0_4
|
||||
# 79| r0_10(signed short) = Load : r0_9, m0_8
|
||||
# 79| valnum = r0_7
|
||||
# 79| r0_11(int) = Convert : r0_10
|
||||
# 79| valnum = unique
|
||||
# 79| r0_12(glval<two_values *>) = VariableAddress[vals] :
|
||||
# 79| valnum = r0_2
|
||||
# 79| r0_13(two_values *) = Load : r0_12, m0_3
|
||||
# 79| valnum = m0_3
|
||||
# 79| r0_14(glval<signed short>) = FieldAddress[val1] : r0_13
|
||||
# 79| valnum = unique
|
||||
# 79| r0_15(signed short) = Load : r0_14, mu0_1
|
||||
# 79| valnum = unique
|
||||
# 79| r0_16(int) = Convert : r0_15
|
||||
# 79| valnum = unique
|
||||
# 79| r0_17(glval<two_values *>) = VariableAddress[vals] :
|
||||
# 79| valnum = r0_2
|
||||
# 79| r0_18(two_values *) = Load : r0_17, m0_3
|
||||
# 79| valnum = m0_3
|
||||
# 79| r0_19(glval<signed short>) = FieldAddress[val2] : r0_18
|
||||
# 79| valnum = unique
|
||||
# 79| r0_20(signed short) = Load : r0_19, mu0_1
|
||||
# 79| valnum = unique
|
||||
# 79| r0_21(int) = Convert : r0_20
|
||||
# 79| valnum = unique
|
||||
# 79| r0_22(int) = Add : r0_16, r0_21
|
||||
# 79| valnum = unique
|
||||
# 79| r0_23(bool) = CompareLT : r0_11, r0_22
|
||||
# 79| valnum = unique
|
||||
# 79| v0_24(void) = ConditionalBranch : r0_23
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
|
||||
# 80| Block 1
|
||||
# 80| r1_0(glval<unknown>) = FunctionAddress[getAValue] :
|
||||
# 80| valnum = unique
|
||||
# 80| r1_1(int) = Call : r1_0
|
||||
# 80| valnum = unique
|
||||
# 80| r1_2(signed short) = Convert : r1_1
|
||||
# 80| valnum = r1_2
|
||||
# 80| r1_3(glval<signed short>) = VariableAddress[v] :
|
||||
# 80| valnum = r0_4
|
||||
# 80| m1_4(signed short) = Store : r1_3, r1_2
|
||||
# 80| valnum = r1_2
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 82| Block 2
|
||||
# 82| v2_0(void) = NoOp :
|
||||
# 75| v2_1(void) = ReturnVoid :
|
||||
# 75| v2_2(void) = UnmodeledUse : mu*
|
||||
# 75| v2_3(void) = ExitFunction :
|
||||
|
||||
# 84| test05(int, int, void *) -> void
|
||||
# 84| Block 0
|
||||
# 84| v0_0(void) = EnterFunction :
|
||||
# 84| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 84| valnum = unique
|
||||
# 84| r0_2(glval<int>) = VariableAddress[x] :
|
||||
# 84| valnum = r0_2
|
||||
# 84| m0_3(int) = InitializeParameter[x] : r0_2
|
||||
# 84| valnum = m0_3
|
||||
# 84| r0_4(glval<int>) = VariableAddress[y] :
|
||||
# 84| valnum = r0_4
|
||||
# 84| m0_5(int) = InitializeParameter[y] : r0_4
|
||||
# 84| valnum = m0_5
|
||||
# 84| r0_6(glval<void *>) = VariableAddress[p] :
|
||||
# 84| valnum = r0_6
|
||||
# 84| m0_7(void *) = InitializeParameter[p] : r0_6
|
||||
# 84| valnum = m0_7
|
||||
# 86| r0_8(glval<int>) = VariableAddress[v] :
|
||||
# 86| valnum = r0_8
|
||||
# 86| m0_9(int) = Uninitialized : r0_8
|
||||
# 86| valnum = unique
|
||||
# 88| r0_10(glval<void *>) = VariableAddress[p] :
|
||||
# 88| valnum = r0_6
|
||||
# 88| r0_11(void *) = Load : r0_10, m0_7
|
||||
# 88| valnum = m0_7
|
||||
# 88| r0_12(void *) = Constant[0] :
|
||||
# 88| valnum = unique
|
||||
# 88| r0_13(bool) = CompareNE : r0_11, r0_12
|
||||
# 88| valnum = unique
|
||||
# 88| v0_14(void) = ConditionalBranch : r0_13
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
|
||||
# 88| Block 1
|
||||
# 88| r1_0(glval<int>) = VariableAddress[x] :
|
||||
# 88| valnum = r0_2
|
||||
# 88| r1_1(int) = Load : r1_0, m0_3
|
||||
# 88| valnum = m0_3
|
||||
# 88| r1_2(glval<int>) = VariableAddress[#temp88:7] :
|
||||
# 88| valnum = r1_2
|
||||
# 88| m1_3(int) = Store : r1_2, r1_1
|
||||
# 88| valnum = m0_3
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 88| Block 2
|
||||
# 88| r2_0(glval<int>) = VariableAddress[y] :
|
||||
# 88| valnum = r0_4
|
||||
# 88| r2_1(int) = Load : r2_0, m0_5
|
||||
# 88| valnum = m0_5
|
||||
# 88| r2_2(glval<int>) = VariableAddress[#temp88:7] :
|
||||
# 88| valnum = r1_2
|
||||
# 88| m2_3(int) = Store : r2_2, r2_1
|
||||
# 88| valnum = m0_5
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 88| Block 3
|
||||
# 88| m3_0(int) = Phi : from 1:m1_3, from 2:m2_3
|
||||
# 88| valnum = unique
|
||||
# 88| r3_1(glval<int>) = VariableAddress[#temp88:7] :
|
||||
# 88| valnum = r1_2
|
||||
# 88| r3_2(int) = Load : r3_1, m3_0
|
||||
# 88| valnum = r3_2
|
||||
# 88| r3_3(glval<int>) = VariableAddress[v] :
|
||||
# 88| valnum = r0_8
|
||||
# 88| m3_4(int) = Store : r3_3, r3_2
|
||||
# 88| valnum = r3_2
|
||||
# 89| v3_5(void) = NoOp :
|
||||
# 84| v3_6(void) = ReturnVoid :
|
||||
# 84| v3_7(void) = UnmodeledUse : mu*
|
||||
# 84| v3_8(void) = ExitFunction :
|
||||
|
||||
# 91| regression_test00() -> int
|
||||
# 91| Block 0
|
||||
# 91| v0_0(void) = EnterFunction :
|
||||
# 91| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 91| valnum = unique
|
||||
# 92| r0_2(glval<int>) = VariableAddress[x] :
|
||||
# 92| valnum = r0_2
|
||||
# 92| r0_3(int) = Constant[10] :
|
||||
# 92| valnum = r0_3
|
||||
# 92| r0_4(glval<int>) = VariableAddress[x] :
|
||||
# 92| valnum = r0_2
|
||||
# 92| m0_5(int) = Store : r0_4, r0_3
|
||||
# 92| valnum = r0_3
|
||||
# 92| m0_6(int) = Store : r0_2, r0_3
|
||||
# 92| valnum = r0_3
|
||||
# 93| r0_7(glval<int>) = VariableAddress[#return] :
|
||||
# 93| valnum = r0_7
|
||||
# 93| r0_8(glval<int>) = VariableAddress[x] :
|
||||
# 93| valnum = r0_2
|
||||
# 93| r0_9(int) = Load : r0_8, m0_6
|
||||
# 93| valnum = r0_3
|
||||
# 93| m0_10(int) = Store : r0_7, r0_9
|
||||
# 93| valnum = r0_3
|
||||
# 91| r0_11(glval<int>) = VariableAddress[#return] :
|
||||
# 91| valnum = r0_7
|
||||
# 91| v0_12(void) = ReturnValue : r0_11, m0_10
|
||||
# 91| v0_13(void) = UnmodeledUse : mu*
|
||||
# 91| v0_14(void) = ExitFunction :
|
||||
|
||||
# 104| inheritanceConversions(Derived *) -> int
|
||||
# 104| Block 0
|
||||
# 104| v0_0(void) = EnterFunction :
|
||||
# 104| mu0_1(unknown) = UnmodeledDefinition :
|
||||
# 104| valnum = unique
|
||||
# 104| r0_2(glval<Derived *>) = VariableAddress[pd] :
|
||||
# 104| valnum = r0_2
|
||||
# 104| m0_3(Derived *) = InitializeParameter[pd] : r0_2
|
||||
# 104| valnum = m0_3
|
||||
# 105| r0_4(glval<int>) = VariableAddress[x] :
|
||||
# 105| valnum = unique
|
||||
# 105| r0_5(glval<Derived *>) = VariableAddress[pd] :
|
||||
# 105| valnum = r0_2
|
||||
# 105| r0_6(Derived *) = Load : r0_5, m0_3
|
||||
# 105| valnum = m0_3
|
||||
# 105| r0_7(Base *) = ConvertToBase[Derived : Base] : r0_6
|
||||
# 105| valnum = r0_7
|
||||
# 105| r0_8(glval<int>) = FieldAddress[b] : r0_7
|
||||
# 105| valnum = r0_8
|
||||
# 105| r0_9(int) = Load : r0_8, mu0_1
|
||||
# 105| valnum = r0_9
|
||||
# 105| m0_10(int) = Store : r0_4, r0_9
|
||||
# 105| valnum = r0_9
|
||||
# 106| r0_11(glval<Base *>) = VariableAddress[pb] :
|
||||
# 106| valnum = r0_11
|
||||
# 106| r0_12(glval<Derived *>) = VariableAddress[pd] :
|
||||
# 106| valnum = r0_2
|
||||
# 106| r0_13(Derived *) = Load : r0_12, m0_3
|
||||
# 106| valnum = m0_3
|
||||
# 106| r0_14(Base *) = ConvertToBase[Derived : Base] : r0_13
|
||||
# 106| valnum = r0_7
|
||||
# 106| m0_15(Base *) = Store : r0_11, r0_14
|
||||
# 106| valnum = r0_7
|
||||
# 107| r0_16(glval<int>) = VariableAddress[y] :
|
||||
# 107| valnum = r0_16
|
||||
# 107| r0_17(glval<Base *>) = VariableAddress[pb] :
|
||||
# 107| valnum = r0_11
|
||||
# 107| r0_18(Base *) = Load : r0_17, m0_15
|
||||
# 107| valnum = r0_7
|
||||
# 107| r0_19(glval<int>) = FieldAddress[b] : r0_18
|
||||
# 107| valnum = r0_8
|
||||
# 107| r0_20(int) = Load : r0_19, mu0_1
|
||||
# 107| valnum = r0_20
|
||||
# 107| m0_21(int) = Store : r0_16, r0_20
|
||||
# 107| valnum = r0_20
|
||||
# 109| r0_22(glval<int>) = VariableAddress[#return] :
|
||||
# 109| valnum = r0_22
|
||||
# 109| r0_23(glval<int>) = VariableAddress[y] :
|
||||
# 109| valnum = r0_16
|
||||
# 109| r0_24(int) = Load : r0_23, m0_21
|
||||
# 109| valnum = r0_20
|
||||
# 109| m0_25(int) = Store : r0_22, r0_24
|
||||
# 109| valnum = r0_20
|
||||
# 104| r0_26(glval<int>) = VariableAddress[#return] :
|
||||
# 104| valnum = r0_22
|
||||
# 104| v0_27(void) = ReturnValue : r0_26, m0_25
|
||||
# 104| v0_28(void) = UnmodeledUse : mu*
|
||||
# 104| v0_29(void) = ExitFunction :
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @kind graph
|
||||
*/
|
||||
import semmle.code.cpp.ir.PrintIR
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
@@ -92,3 +92,19 @@ int regression_test00() {
|
||||
int x = x = 10;
|
||||
return x;
|
||||
}
|
||||
|
||||
struct Base {
|
||||
int b;
|
||||
};
|
||||
|
||||
struct Derived : Base {
|
||||
int d;
|
||||
};
|
||||
|
||||
int inheritanceConversions(Derived* pd) {
|
||||
int x = pd->b;
|
||||
Base* pb = static_cast<Base*>(pd);
|
||||
int y = pb->b;
|
||||
|
||||
return y;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
| test.cpp:5:3:5:3 | x | 5:c3-c3 6:c3-c3 7:c7-c7 |
|
||||
| test.cpp:5:7:5:8 | p0 | 5:c7-c8 6:c7-c8 |
|
||||
| test.cpp:5:7:5:13 | ... + ... | 5:c7-c13 6:c7-c13 |
|
||||
| test.cpp:5:12:5:13 | p1 | 5:c12-c13 6:c12-c13 |
|
||||
| test.cpp:16:3:16:3 | x | 16:c3-c3 17:c3-c3 18:c7-c7 |
|
||||
| test.cpp:16:7:16:8 | p0 | 16:c7-c8 17:c7-c8 |
|
||||
| test.cpp:16:7:16:13 | ... + ... | 16:c7-c13 17:c7-c13 |
|
||||
| test.cpp:16:7:16:24 | ... + ... | 16:c7-c24 17:c7-c24 |
|
||||
| test.cpp:16:12:16:13 | p1 | 16:c12-c13 17:c12-c13 |
|
||||
| test.cpp:16:17:16:24 | global01 | 16:c17-c24 17:c17-c24 |
|
||||
| test.cpp:29:3:29:3 | x | 29:c3-c3 31:c3-c3 32:c7-c7 |
|
||||
| test.cpp:29:7:29:8 | p0 | 29:c7-c8 31:c7-c8 |
|
||||
| test.cpp:29:7:29:13 | ... + ... | 29:c7-c13 31:c7-c13 |
|
||||
| test.cpp:29:7:29:24 | ... + ... | 29:c7-c24 31:c7-c24 |
|
||||
| test.cpp:29:12:29:13 | p1 | 29:c12-c13 31:c12-c13 |
|
||||
| test.cpp:29:17:29:24 | global02 | 29:c17-c24 31:c17-c24 |
|
||||
| test.cpp:43:3:43:3 | x | 43:c3-c3 45:c3-c3 46:c7-c7 |
|
||||
| test.cpp:43:7:43:8 | p0 | 43:c7-c8 45:c7-c8 |
|
||||
| test.cpp:43:7:43:13 | ... + ... | 43:c7-c13 45:c7-c13 |
|
||||
| test.cpp:43:7:43:24 | ... + ... | 43:c7-c24 45:c7-c24 |
|
||||
| test.cpp:43:12:43:13 | p1 | 43:c12-c13 45:c12-c13 |
|
||||
| test.cpp:43:17:43:24 | global03 | 43:c17-c24 45:c17-c24 |
|
||||
| test.cpp:53:10:53:13 | (int)... | 53:c10-c13 56:c21-c24 |
|
||||
| test.cpp:53:10:53:13 | * ... | 53:c10-c13 56:c21-c24 |
|
||||
| test.cpp:53:11:53:13 | str | 53:c11-c13 56:c22-c24 |
|
||||
| test.cpp:55:5:55:7 | ptr | 55:c5-c7 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-c12 |
|
||||
| test.cpp:59:9:59:12 | (int)... | 56:c13-c16 56:c31-c34 59:c9-c12 |
|
||||
| test.cpp:59:9:59:12 | * ... | 56:c13-c16 56:c31-c34 59:c9-c12 |
|
||||
| test.cpp:59:17:59:20 | 0 | 53:c18-c21 56:c39-c42 59:c17-c20 |
|
||||
| test.cpp:59:17:59:20 | (int)... | 53:c18-c21 56:c39-c42 59:c17-c20 |
|
||||
| test.cpp:62:5:62:10 | result | 62:c5-c10 65:c10-c15 |
|
||||
| test.cpp:79:11:79:14 | vals | 79:c11-c14 79:c24-c27 |
|
||||
| test.cpp:80:5:80:5 | v | 79:c7-c7 80:c5-c5 |
|
||||
| test.cpp:80:9:80:17 | call to getAValue | 77:c20-c28 80:c9-c17 |
|
||||
| test.cpp:80:9:80:19 | (signed short)... | 77:c20-c30 80:c9-c19 |
|
||||
| test.cpp:92:15:92:16 | 10 | 260:c21-c22 261:c21-c22 92:c15-c16 |
|
||||
| test.cpp:93:10:93:10 | x | 92:c11-c11 93:c10-c10 |
|
||||
| test.cpp:97:3:97:3 | x | 97:c3-c3 98:c3-c3 |
|
||||
| test.cpp:97:3:97:5 | ... ++ | 97:c3-c5 98:c3-c5 |
|
||||
| test.cpp:104:3:104:3 | x | 104:c3-c3 105:c3-c3 106:c3-c3 107:c3-c3 108:c3-c3 |
|
||||
| test.cpp:107:7:107:11 | ... + ... | 107:c7-c11 108:c7-c11 |
|
||||
| test.cpp:111:3:111:5 | str | 111:c3-c5 112:c3-c5 113:c3-c5 |
|
||||
| test.cpp:111:9:111:11 | 1 | 110:c15-c17 111:c9-c11 |
|
||||
| test.cpp:111:9:111:11 | (char *)... | 110:c15-c17 111:c9-c11 |
|
||||
| test.cpp:111:9:111:11 | array to pointer conversion | 110:c15-c17 111:c9-c11 |
|
||||
| test.cpp:112:9:112:11 | 2 | 112:c9-c11 113:c9-c11 |
|
||||
| test.cpp:112:9:112:11 | (char *)... | 112:c9-c11 113:c9-c11 |
|
||||
| test.cpp:112:9:112:11 | array to pointer conversion | 112:c9-c11 113:c9-c11 |
|
||||
| test.cpp:116:3:116:3 | y | 116:c3-c3 117:c3-c3 118:c3-c3 |
|
||||
| test.cpp:116:7:116:9 | 0.0 | 115:c13-c15 116:c7-c9 |
|
||||
| test.cpp:116:7:116:9 | (float)... | 115:c13-c15 116:c7-c9 |
|
||||
| test.cpp:117:7:117:9 | 0.5 | 117:c7-c9 118:c7-c9 |
|
||||
| test.cpp:117:7:117:9 | (float)... | 117:c7-c9 118:c7-c9 |
|
||||
| test.cpp:122:3:122:8 | call to test07 | 122:c3-c8 123:c3-c8 |
|
||||
| test.cpp:125:3:125:11 | call to my_strspn | 125:c3-c11 126:c3-c11 |
|
||||
| test.cpp:125:13:125:17 | array to pointer conversion | 125:c13-c17 126:c13-c17 129:c20-c24 |
|
||||
| test.cpp:125:13:125:17 | foo | 125:c13-c17 126:c13-c17 129:c20-c24 |
|
||||
| test.cpp:129:13:129:17 | array to pointer conversion | 125:c20-c24 126:c20-c24 129:c13-c17 |
|
||||
| test.cpp:129:13:129:17 | bar | 125:c20-c24 126:c20-c24 129:c13-c17 |
|
||||
| test.cpp:141:12:141:17 | call to getInt | 141:c12-c17 141:c29-c34 |
|
||||
| test.cpp:141:23:141:26 | this | 0:c0-c0 141:c23-c26 |
|
||||
| test.cpp:146:10:146:11 | ih | 146:c10-c11 146:c31-c32 |
|
||||
| test.cpp:146:13:146:25 | call to getDoubledInt | 146:c13-c25 146:c34-c46 |
|
||||
| test.cpp:150:3:150:3 | x | 150:c3-c3 150:c9-c9 151:c3-c3 151:c9-c9 152:c12-c12 |
|
||||
| test.cpp:150:3:150:5 | ... ++ | 150:c3-c5 150:c9-c11 151:c3-c5 151:c9-c11 152:c10-c12 |
|
||||
| test.cpp:150:3:150:11 | ... + ... | 150:c3-c11 151:c3-c11 |
|
||||
| test.cpp:156:3:156:9 | 0 | 156:c14-c20 156:c3-c9 157:c10-c16 |
|
||||
| test.cpp:171:3:171:6 | (int)... | 171:c3-c6 172:c3-c6 |
|
||||
| test.cpp:171:3:171:6 | e1x1 | 171:c3-c6 172:c3-c6 |
|
||||
| test.cpp:179:10:179:22 | (...) | 179:c10-c22 179:c10-c22 |
|
||||
| test.cpp:179:17:179:17 | y | 179:c17-c17 179:c17-c17 |
|
||||
| test.cpp:179:17:179:21 | ... + ... | 179:c17-c21 179:c17-c21 |
|
||||
| test.cpp:185:17:185:17 | y | 185:c17-c17 185:c17-c17 |
|
||||
| test.cpp:202:3:202:18 | sizeof(padded_t) | 202:c3-c18 204:c3-c18 |
|
||||
| test.cpp:205:24:205:34 | sizeof(int) | 205:c24-c34 245:c25-c35 246:c25-c35 |
|
||||
| test.cpp:206:3:206:21 | alignof(int_holder) | 206:c25-c43 206:c3-c21 |
|
||||
| test.cpp:209:3:209:18 | sizeof(<expr>) | 209:c22-c37 209:c3-c18 |
|
||||
| test.cpp:209:10:209:15 | holder | 209:c10-c15 209:c29-c34 |
|
||||
| test.cpp:209:10:209:18 | (...) | 209:c10-c18 209:c29-c37 |
|
||||
| test.cpp:209:17:209:17 | x | 209:c17-c17 209:c36-c36 |
|
||||
| test.cpp:210:10:210:10 | x | 208:c27-c27 210:c10-c10 211:c11-c11 211:c24-c24 |
|
||||
| test.cpp:210:10:210:11 | (...) | 210:c10-c11 211:c11-c12 211:c24-c25 |
|
||||
| test.cpp:211:3:211:12 | alignof(<expr>) | 211:c16-c25 211:c3-c12 |
|
||||
| test.cpp:239:3:239:12 | new | 239:c3-c12 240:c3-c12 |
|
||||
| test.cpp:245:16:245:36 | new[] | 245:c16-c36 246:c16-c36 |
|
||||
| test.cpp:248:3:248:28 | delete | 248:c3-c28 249:c3-c28 |
|
||||
| test.cpp:248:10:248:28 | call to operator new | 248:c10-c28 249:c10-c28 |
|
||||
| test.cpp:248:10:248:28 | new | 248:c10-c28 249:c10-c28 |
|
||||
| test.cpp:252:10:252:32 | call to operator new | 252:c10-c32 253:c12-c34 |
|
||||
| test.cpp:252:10:252:32 | new | 252:c10-c32 253:c12-c34 |
|
||||
| test.cpp:254:3:254:25 | <error expr> | 248:c10-c28 249:c10-c28 250:c10-c28 252:c10-c32 253:c12-c34 254:c3-c25 255:c3-c25 257:c3-c19 258:c3-c19 260:c3-c23 261:c3-c23 |
|
||||
| test.cpp:254:7:254:8 | 32 | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 |
|
||||
| test.cpp:254:7:254:8 | (size_t)... | 252:c14-c15 253:c16-c17 254:c7-c8 257:c7-c8 258:c7-c8 260:c7-c8 261:c7-c8 263:c16-c17 264:c16-c17 265:c16-c17 266:c7-c8 267:c7-c8 269:c7-c8 270:c7-c8 271:c7-c8 |
|
||||
| test.cpp:254:11:254:14 | (void *)... | 250:c14-c17 254:c11-c14 |
|
||||
| test.cpp:254:11:254:14 | ptr2 | 250:c14-c17 254:c11-c14 |
|
||||
| test.cpp:255:11:255:14 | (void *)... | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 |
|
||||
| test.cpp:255:11:255:14 | ptr1 | 248:c14-c17 249:c14-c17 252:c18-c21 253:c20-c23 255:c11-c14 |
|
||||
| test.cpp:257:3:257:19 | call to operator new | 257:c3-c19 258:c3-c19 |
|
||||
| test.cpp:257:3:257:19 | new | 257:c3-c19 258:c3-c19 |
|
||||
| test.cpp:260:3:260:23 | call to operator new[] | 260:c3-c23 261:c3-c23 |
|
||||
| test.cpp:260:3:260:23 | new[] | 260:c3-c23 261:c3-c23 |
|
||||
| test.cpp:263:3:263:32 | delete[] | 263:c3-c32 264:c3-c32 |
|
||||
| test.cpp:263:12:263:32 | new[] | 263:c12-c32 264:c12-c32 |
|
||||
| test.cpp:263:12:263:32 | {...} | 263:c12-c32 264:c12-c32 |
|
||||
| test.cpp:266:3:266:23 | <error expr> | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 |
|
||||
| test.cpp:266:3:266:23 | call to operator new[] | 263:c12-c32 264:c12-c32 265:c12-c32 266:c3-c23 267:c3-c23 269:c3-c19 270:c3-c19 271:c3-c19 |
|
||||
| test.cpp:269:3:269:19 | new[] | 269:c3-c19 270:c3-c19 |
|
||||
| test.cpp:269:3:269:19 | {...} | 269:c3-c19 270:c3-c19 |
|
||||
| test.cpp:271:15:271:15 | 3 | 265:c28-c28 271:c15-c15 35:c16-c16 |
|
||||
| test.cpp:273:3:273:12 | new[] | 273:c3-c12 274:c3-c12 |
|
||||
| test.cpp:273:11:273:11 | x | 273:c11-c11 274:c11-c11 |
|
||||
| test.cpp:285:5:285:5 | 1 | 103:c10-c11 104:c7-c7 107:c7-c7 108:c7-c7 10:c16-c16 179:c21-c21 239:c11-c11 240:c11-c11 263:c28-c28 264:c28-c28 266:c19-c19 266:c22-c22 285:c5-c5 289:c5-c5 294:c5-c5 299:c9-c9 300:c9-c9 310:c5-c5 311:c5-c5 313:c5-c5 |
|
||||
| test.cpp:286:5:286:5 | 2 | 105:c7-c7 106:c7-c7 107:c11-c11 108:c11-c11 21:c16-c16 241:c11-c11 263:c24-c24 263:c31-c31 264:c24-c24 264:c31-c31 265:c24-c24 266:c15-c15 267:c15-c15 267:c19-c19 267:c22-c22 269:c15-c15 270:c15-c15 286:c5-c5 290:c5-c5 293:c5-c5 301:c9-c9 302:c9-c9 |
|
||||
| test.cpp:299:3:299:9 | throw ... | 299:c3-c9 300:c3-c9 |
|
||||
| test.cpp:301:3:301:9 | throw ... | 301:c3-c9 302:c3-c9 |
|
||||
| test.cpp:303:3:303:7 | re-throw exception | 303:c3-c7 304:c3-c7 |
|
||||
| test.cpp:308:3:308:3 | x | 308:c3-c3 309:c3-c3 310:c3-c3 311:c3-c3 |
|
||||
| test.cpp:308:3:308:6 | access to array | 308:c3-c6 309:c3-c6 |
|
||||
| test.cpp:308:5:308:5 | 0 | 308:c5-c5 309:c5-c5 312:c5-c5 44:c9-c9 51:c25-c25 88:c12-c12 |
|
||||
| test.cpp:310:3:310:6 | access to array | 310:c3-c6 311:c3-c6 |
|
||||
| test.cpp:312:3:312:3 | y | 312:c3-c3 313:c3-c3 |
|
||||
| test.cpp:320:3:320:11 | test_18_p | 320:c3-c11 321:c3-c11 |
|
||||
| test.cpp:320:3:320:13 | call to expression | 320:c3-c13 321:c3-c13 |
|
||||
| test.cpp:324:3:324:11 | test_19_p | 324:c3-c11 325:c3-c11 326:c3-c11 |
|
||||
| test.cpp:324:3:324:17 | call to expression | 324:c3-c17 325:c3-c17 |
|
||||
| test.cpp:324:13:324:13 | x | 324:c13-c13 325:c13-c13 326:c16-c16 |
|
||||
| test.cpp:326:13:326:13 | y | 324:c16-c16 325:c16-c16 326:c13-c13 |
|
||||
| test.cpp:330:3:330:3 | x | 330:c12-c12 330:c3-c3 331:c12-c12 331:c3-c3 332:c12-c12 332:c8-c8 333:c16-c16 333:c3-c3 |
|
||||
| test.cpp:330:3:330:8 | ... == ... | 330:c3-c8 331:c3-c8 333:c3-c8 |
|
||||
| test.cpp:330:3:330:16 | ... ? ... : ... | 330:c3-c16 331:c3-c16 |
|
||||
| test.cpp:332:3:332:3 | y | 330:c16-c16 330:c8-c8 331:c16-c16 331:c8-c8 332:c16-c16 332:c3-c3 333:c12-c12 333:c8-c8 |
|
||||
@@ -0,0 +1,12 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
from HashCons h
|
||||
where strictcount(h.getAnExpr()) > 1
|
||||
select
|
||||
h,
|
||||
strictconcat(Location loc
|
||||
| loc = h.getAnExpr().getLocation()
|
||||
| loc.getStartLine() +
|
||||
":c" + loc.getStartColumn() + "-c" + loc.getEndColumn()
|
||||
, " ")
|
||||
@@ -0,0 +1,8 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
// Every expression should have exactly one HC.
|
||||
// So this query should have zero results.
|
||||
from Expr e
|
||||
where count(hashCons(e)) != 1
|
||||
select e, concat(HashCons h | h = hashCons(e) | h.getKind(), ", ")
|
||||
334
cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp
Normal file
334
cpp/ql/test/library-tests/valuenumbering/HashCons/test.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
int test00(int p0, int p1) {
|
||||
int x, y;
|
||||
unsigned char b;
|
||||
|
||||
x = p0 + p1;
|
||||
x = p0 + p1; // Same value as previous line. Should the assignment also be matched?
|
||||
y = x;
|
||||
}
|
||||
|
||||
int global01 = 1;
|
||||
|
||||
int test01(int p0, int p1) {
|
||||
int x, y;
|
||||
unsigned char b;
|
||||
|
||||
x = p0 + p1 + global01;
|
||||
x = p0 + p1 + global01; // Same structure as previous line.
|
||||
y = x; // x is the same as x above
|
||||
}
|
||||
|
||||
int global02 = 2;
|
||||
|
||||
void change_global02(); // Just a declaration
|
||||
|
||||
int test02(int p0, int p1) {
|
||||
int x, y;
|
||||
unsigned char b;
|
||||
|
||||
x = p0 + p1 + global02;
|
||||
change_global02();
|
||||
x = p0 + p1 + global02; // same HashCons as above
|
||||
y = x;
|
||||
}
|
||||
|
||||
int global03 = 3;
|
||||
|
||||
void change_global03(); // Just a declaration
|
||||
|
||||
int test03(int p0, int p1, int* p2) {
|
||||
int x, y;
|
||||
unsigned char b;
|
||||
|
||||
x = p0 + p1 + global03;
|
||||
*p2 = 0;
|
||||
x = p0 + p1 + global03; // same HashCons as 43
|
||||
y = x;
|
||||
}
|
||||
|
||||
unsigned int my_strspn(const char *str, const char *chars) {
|
||||
const char *ptr;
|
||||
unsigned int result = 0;
|
||||
|
||||
while (*str != '\0') {
|
||||
// check *str against chars
|
||||
ptr = chars;
|
||||
while ((*ptr != *str) && (*ptr != '\0')) {ptr++;}
|
||||
|
||||
// update
|
||||
if (*ptr == '\0') { // ptr same as ptr on lines 53 and 56
|
||||
break;
|
||||
}
|
||||
result++;
|
||||
}
|
||||
|
||||
return result; // result same as result on line 62
|
||||
}
|
||||
|
||||
int getAValue();
|
||||
|
||||
struct two_values {
|
||||
signed short val1;
|
||||
signed short val2;
|
||||
};
|
||||
|
||||
void test04(two_values *vals)
|
||||
{
|
||||
signed short v = getAValue(); // should this match getAValue() on line 80?
|
||||
|
||||
if (v < vals->val1 + vals->val2) {
|
||||
v = getAValue(); // should this match getAValue() on line 77?
|
||||
}
|
||||
}
|
||||
|
||||
void test05(int x, int y, void *p)
|
||||
{
|
||||
int v;
|
||||
|
||||
v = p != 0 ? x : y;
|
||||
}
|
||||
|
||||
int regression_test00() {
|
||||
int x = x = 10;
|
||||
return x;
|
||||
}
|
||||
|
||||
void test06(int x) {
|
||||
x++;
|
||||
x++; // x++ is matched
|
||||
}
|
||||
|
||||
// literals
|
||||
void test07() {
|
||||
int x = 1;
|
||||
x = 1;
|
||||
x = 2;
|
||||
x = 2;
|
||||
x = 1 + 2;
|
||||
x = 1 + 2;
|
||||
|
||||
char *str = "1";
|
||||
str = "1";
|
||||
str = "2";
|
||||
str = "2";
|
||||
|
||||
float y = 0.0;
|
||||
y = 0.0;
|
||||
y = 0.5;
|
||||
y = 0.5;
|
||||
}
|
||||
|
||||
void test08() {
|
||||
test07();
|
||||
test07();
|
||||
|
||||
my_strspn("foo", "bar");
|
||||
my_strspn("foo", "bar");
|
||||
|
||||
|
||||
my_strspn("bar", "foo");
|
||||
}
|
||||
|
||||
class IntHolder {
|
||||
int myInt;
|
||||
|
||||
int getInt() {
|
||||
return myInt;
|
||||
}
|
||||
|
||||
public:
|
||||
int getDoubledInt() {
|
||||
return getInt() + this->getInt(); // getInt() and this->getInt() should be the same
|
||||
}
|
||||
};
|
||||
|
||||
int test09(IntHolder ih) {
|
||||
return ih.getDoubledInt() + ih.getDoubledInt();
|
||||
}
|
||||
|
||||
int test10(int x) {
|
||||
x++ + x++;
|
||||
x++ + x++; // same as above
|
||||
return ++x; // ++x is not the same as x++
|
||||
}
|
||||
|
||||
void* test11() {
|
||||
nullptr == nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
enum t1 {
|
||||
e1x1 = 1,
|
||||
e1x2 = 2
|
||||
};
|
||||
|
||||
enum t2 {
|
||||
e2x1 = 1,
|
||||
e2x2 = 2
|
||||
};
|
||||
|
||||
int test12() {
|
||||
e1x1 == e2x1;
|
||||
e1x1 == e2x2;
|
||||
return e1x2;
|
||||
}
|
||||
|
||||
#define SQUARE(x) ((x) * (x))
|
||||
|
||||
int test13(int y) {
|
||||
return SQUARE(y + 1);
|
||||
}
|
||||
|
||||
#define SQUARE(x) x * x
|
||||
|
||||
int test14(int y) {
|
||||
return SQUARE(y);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
char y;
|
||||
} padded_t;
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
} int_holder;
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
void *malloc(size_t size);
|
||||
|
||||
int test15(int x) {
|
||||
sizeof(padded_t);
|
||||
alignof(padded_t);
|
||||
sizeof(padded_t);
|
||||
sizeof(int_holder) + sizeof(int);
|
||||
alignof(int_holder) + alignof(int_holder);
|
||||
|
||||
int_holder holder = {x: x};
|
||||
sizeof(holder.x) + sizeof(holder.x);
|
||||
sizeof(x);
|
||||
alignof(x) + alignof(x);
|
||||
}
|
||||
|
||||
static void *operator new(size_t size, void *placement) {
|
||||
return placement;
|
||||
}
|
||||
|
||||
static void *operator new(size_t size, size_t alignment, void *placement) {
|
||||
return placement;
|
||||
}
|
||||
|
||||
static void *operator new(size_t size, size_t alignment) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void *operator new[](size_t size, void *placement) {
|
||||
return placement;
|
||||
}
|
||||
|
||||
static void *operator new[](size_t size, size_t alignment, void *placement) {
|
||||
return placement;
|
||||
}
|
||||
|
||||
static void *operator new[](size_t size, size_t alignment) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void test16(int y, int z) {
|
||||
new int(1);
|
||||
new int(1);
|
||||
new int(2);
|
||||
|
||||
int x;
|
||||
|
||||
char *ptr1 = new char[sizeof(int)];
|
||||
char *ptr2 = new char[sizeof(int)];
|
||||
|
||||
delete new(ptr1) IntHolder;
|
||||
delete new(ptr1) IntHolder;
|
||||
delete new(ptr2) IntHolder;
|
||||
|
||||
delete new(32, ptr1) IntHolder;
|
||||
delete[] new(32, ptr1) IntHolder;
|
||||
new(32, ptr2) IntHolder;
|
||||
new(16, ptr1) IntHolder;
|
||||
|
||||
new(32) IntHolder;
|
||||
new(32) IntHolder;
|
||||
|
||||
new(32) IntHolder[10];
|
||||
new(32) IntHolder[10];
|
||||
|
||||
delete[] new(32) int[2] {1, 2};
|
||||
delete[] new(32) int[2] {1, 2};
|
||||
delete[] new(32) int[2] {3, 4};
|
||||
new(32) int[2] {1, 1};
|
||||
new(32) int[2] {2, 2};
|
||||
|
||||
new(32) int[2] {};
|
||||
new(32) int[2] {};
|
||||
new(32) int[3] {};
|
||||
|
||||
new int[x];
|
||||
new int[x];
|
||||
new int[z];
|
||||
}
|
||||
|
||||
typedef struct point{
|
||||
int x;
|
||||
int y;
|
||||
} point_t;
|
||||
|
||||
void test17() {
|
||||
point_t p1 = {
|
||||
1,
|
||||
2
|
||||
};
|
||||
point_t p2 = {
|
||||
1,
|
||||
2
|
||||
};
|
||||
point_t p3 = {
|
||||
2,
|
||||
1
|
||||
};
|
||||
}
|
||||
|
||||
void test18() {
|
||||
throw 1;
|
||||
throw 1;
|
||||
throw 2;
|
||||
throw 2;
|
||||
throw;
|
||||
throw;
|
||||
}
|
||||
|
||||
void test19(int *x, int *y) {
|
||||
x[0];
|
||||
x[0];
|
||||
x[1];
|
||||
x[1];
|
||||
y[0];
|
||||
y[1];
|
||||
}
|
||||
|
||||
void test20(int *x, int *y) {
|
||||
void (*test_18_p)() = &test18;
|
||||
void (*test_17_p)() = &test17;
|
||||
void (*test_19_p)(int *, int *) = &test19;
|
||||
test_18_p();
|
||||
test_18_p();
|
||||
test_17_p();
|
||||
|
||||
test_19_p(x, y);
|
||||
test_19_p(x, y);
|
||||
test_19_p(y, x);
|
||||
}
|
||||
|
||||
void test21(int x, int y) {
|
||||
x == y ? x : y;
|
||||
x == y ? x : y;
|
||||
y == x ? x : y;
|
||||
x == y ? y : x;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
+ semmlecode-javascript-queries/DOM/TargetBlank.ql: /Security/CWE/CWE-200
|
||||
+ semmlecode-javascript-queries/Electron/EnablingNodeIntegration.ql: /Security/CWE/CWE-094
|
||||
+ semmlecode-javascript-queries/Security/CWE-022/TaintedPath.ql: /Security/CWE/CWE-022
|
||||
+ semmlecode-javascript-queries/Security/CWE-078/CommandInjection.ql: /Security/CWE/CWE-078
|
||||
+ semmlecode-javascript-queries/Security/CWE-079/ReflectedXss.ql: /Security/CWE/CWE-079
|
||||
+ semmlecode-javascript-queries/Security/CWE-079/StoredXss.ql: /Security/CWE/CWE-079
|
||||
+ semmlecode-javascript-queries/Security/CWE-079/Xss.ql: /Security/CWE/CWE-079
|
||||
+ semmlecode-javascript-queries/Security/CWE-089/SqlInjection.ql: /Security/CWE/CWE-089
|
||||
+ semmlecode-javascript-queries/Security/CWE-094/CodeInjection.ql: /Security/CWE/CWE-094
|
||||
|
||||
58
javascript/ql/src/Electron/EnablingNodeIntegration.qhelp
Normal file
58
javascript/ql/src/Electron/EnablingNodeIntegration.qhelp
Normal file
@@ -0,0 +1,58 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Enabling Node.js integration in Electron web content renderers
|
||||
(<code>BrowserWindow</code>, <code>BrowserView</code> and
|
||||
<code>webview</code>) can result in remote native code execution
|
||||
attacks.
|
||||
|
||||
The attack is realized when the renderer uses content from an
|
||||
untrusted remote web site or a trusted site with a cross site
|
||||
scripting vulnerability.
|
||||
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
|
||||
Node.js integration should be disabled when loading remote web
|
||||
sites. Always set <code>nodeIntegration</code> preference
|
||||
to <code>false</code> before loading remote web sites, and only enable
|
||||
it for whitelisted sites.
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
Note that the <code>nodeIntegration</code> property is enabled
|
||||
by default in Electron and needs to be set to <code>false</code>
|
||||
explicitly.
|
||||
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
|
||||
The following examples shows insecure and secure uses of
|
||||
<code>BrowserWindow</code> and <code>BrowserView</code> when loading
|
||||
remote web sites:
|
||||
|
||||
</p>
|
||||
|
||||
<sample src="examples/EnablingNodeIntegration.js"/>
|
||||
|
||||
</example>
|
||||
|
||||
|
||||
<references>
|
||||
<li>Electron Documentation: <a href="https://electronjs.org/docs/tutorial/security#2-disable-nodejs-integration-for-remote-content">Security, Native Capabilities, and Your Responsibility</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
34
javascript/ql/src/Electron/EnablingNodeIntegration.ql
Normal file
34
javascript/ql/src/Electron/EnablingNodeIntegration.ql
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @name Enabling Node.js integration for Electron web content renderers
|
||||
* @description Enabling `nodeIntegration` or `nodeIntegrationInWorker` can expose the application to remote code execution.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision low
|
||||
* @id js/enabling-electron-renderer-node-integration
|
||||
* @tags security
|
||||
* frameworks/electron
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Gets a warning message for `pref` if one of the `nodeIntegration` features is enabled.
|
||||
*/
|
||||
string getNodeIntegrationWarning(Electron::WebPreferences pref) {
|
||||
exists (string feature |
|
||||
feature = "nodeIntegration" or
|
||||
feature = "nodeIntegrationInWorker" |
|
||||
pref.getAPropertyWrite(feature).getRhs().mayHaveBooleanValue(true) and
|
||||
result = "The `" + feature + "` feature has been enabled."
|
||||
)
|
||||
or
|
||||
exists (string feature |
|
||||
feature = "nodeIntegration" |
|
||||
not exists(pref.getAPropertyWrite(feature)) and
|
||||
result = "The `" + feature + "` feature is enabled by default."
|
||||
)
|
||||
}
|
||||
|
||||
from Electron::WebPreferences preferences
|
||||
select preferences, getNodeIntegrationWarning(preferences)
|
||||
@@ -0,0 +1,21 @@
|
||||
//BAD: `nodeIntegration` enabled by default
|
||||
var win_1 = new BrowserWindow();
|
||||
win_1.loadURL(remote_site);
|
||||
|
||||
//BAD: `nodeIntegration` enabled
|
||||
var win_2 = new BrowserWindow({webPreferences: {nodeIntegration: true}});
|
||||
win_2.loadURL(remote_site);
|
||||
|
||||
//GOOD: `nodeIntegration` disabled
|
||||
let win_3 = new BrowserWindow({webPreferences: {nodeIntegration: false}});
|
||||
win_3.loadURL(remote_site);
|
||||
|
||||
//BAD: `nodeIntegration` enabled in the view
|
||||
var win_4 = new BrowserWindow({webPreferences: {nodeIntegration: false}})
|
||||
var view_4 = new BrowserView({
|
||||
webPreferences: {
|
||||
nodeIntegration: true
|
||||
}
|
||||
});
|
||||
win_4.setBrowserView(view_4);
|
||||
view_4.webContents.loadURL(remote_site);
|
||||
@@ -10,20 +10,48 @@
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if the receiver of `method` is bound in a method of its class.
|
||||
* Holds if the receiver of `method` is bound.
|
||||
*/
|
||||
private predicate isBoundInMethod(MethodDeclaration method) {
|
||||
exists (DataFlow::ThisNode thiz, MethodDeclaration bindingMethod |
|
||||
bindingMethod.getDeclaringClass() = method.getDeclaringClass() and
|
||||
not bindingMethod.isStatic() and
|
||||
thiz.getBinder().getAstNode() = bindingMethod.getBody() and
|
||||
exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind |
|
||||
// this.<methodName> = <expr>.bind(...)
|
||||
thiz.hasPropertyWrite(method.getName(), rhs) and
|
||||
bind.flowsTo(rhs) and
|
||||
bind.getMethodName() = "bind"
|
||||
thiz.getBinder().getAstNode() = bindingMethod.getBody() |
|
||||
// require("auto-bind")(this)
|
||||
thiz.flowsTo(DataFlow::moduleImport("auto-bind").getACall().getArgument(0))
|
||||
or
|
||||
exists (string name |
|
||||
name = method.getName() |
|
||||
exists (DataFlow::Node rhs, DataFlow::MethodCallNode bind |
|
||||
// this.<methodName> = <expr>.bind(...)
|
||||
thiz.hasPropertyWrite(name, rhs) and
|
||||
bind.flowsTo(rhs) and
|
||||
bind.getMethodName() = "bind"
|
||||
)
|
||||
or
|
||||
exists (DataFlow::MethodCallNode bindAll |
|
||||
bindAll.getMethodName() = "bindAll" and
|
||||
thiz.flowsTo(bindAll.getArgument(0)) |
|
||||
// _.bindAll(this, <name1>)
|
||||
bindAll.getArgument(1).mayHaveStringValue(name)
|
||||
or
|
||||
// _.bindAll(this, [<name1>, <name2>])
|
||||
exists (DataFlow::ArrayLiteralNode names |
|
||||
names.flowsTo(bindAll.getArgument(1)) and
|
||||
names.getAnElement().mayHaveStringValue(name)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists (Expr decoration, string name |
|
||||
decoration = method.getADecorator().getExpression() and
|
||||
name.regexpMatch("(?i).*(bind|bound).*") |
|
||||
// @autobind
|
||||
decoration.(Identifier).getName() = name or
|
||||
// @action.bound
|
||||
decoration.(PropAccess).getPropertyName() = name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
63
javascript/ql/src/Security/CWE-079/StoredXss.qhelp
Normal file
63
javascript/ql/src/Security/CWE-079/StoredXss.qhelp
Normal file
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
|
||||
Directly using uncontrolled stored value (for example, file names) to
|
||||
create HTML content without properly sanitizing the input first,
|
||||
allows for a cross-site scripting vulnerability.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
This kind of vulnerability is also called <i>stored</i> cross-site
|
||||
scripting, to distinguish it from other types of cross-site scripting.
|
||||
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
|
||||
To guard against cross-site scripting, consider using contextual
|
||||
output encoding/escaping before using uncontrolled stored values to
|
||||
create HTML content, or one of the other solutions that are mentioned
|
||||
in the references.
|
||||
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
|
||||
The following example code writes file names directly to a HTTP
|
||||
response. This leaves the website vulnerable to cross-site scripting,
|
||||
if an attacker can choose the file names on the disk.
|
||||
|
||||
</p>
|
||||
<sample src="examples/StoredXss.js" />
|
||||
<p>
|
||||
Sanitizing the file names prevents the vulnerability:
|
||||
</p>
|
||||
<sample src="examples/StoredXssGood.js" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet">XSS
|
||||
(Cross Site Scripting) Prevention Cheat Sheet</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP
|
||||
<a href="https://www.owasp.org/index.php/Types_of_Cross-Site_Scripting">Types of Cross-Site
|
||||
Scripting</a>.
|
||||
</li>
|
||||
<li>
|
||||
Wikipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
20
javascript/ql/src/Security/CWE-079/StoredXss.ql
Normal file
20
javascript/ql/src/Security/CWE-079/StoredXss.ql
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Stored cross-site scripting
|
||||
* @description Using uncontrolled stored values in HTML allows for
|
||||
* a stored cross-site scripting vulnerability.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id js/stored-xss
|
||||
* @tags security
|
||||
* external/cwe/cwe-079
|
||||
* external/cwe/cwe-116
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.StoredXss::StoredXss
|
||||
|
||||
from Configuration xss, DataFlow::Node source, DataFlow::Node sink
|
||||
where xss.hasFlow(source, sink)
|
||||
select sink, "Stored cross-site scripting vulnerability due to $@.",
|
||||
source, "stored value"
|
||||
14
javascript/ql/src/Security/CWE-079/examples/StoredXss.js
Normal file
14
javascript/ql/src/Security/CWE-079/examples/StoredXss.js
Normal file
@@ -0,0 +1,14 @@
|
||||
var express = require('express'),
|
||||
fs = require('fs');
|
||||
|
||||
express().get('/list-directory', function(req, res) {
|
||||
fs.readdir('/public', function (error, fileNames) {
|
||||
var list = '<ul>';
|
||||
fileNames.forEach(fileName => {
|
||||
// BAD: `fileName` can contain HTML elements
|
||||
list += '<li>' + fileName '</li>';
|
||||
});
|
||||
list += '</ul>'
|
||||
res.send(list);
|
||||
});
|
||||
});
|
||||
15
javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js
Normal file
15
javascript/ql/src/Security/CWE-079/examples/StoredXssGood.js
Normal file
@@ -0,0 +1,15 @@
|
||||
var express = require('express'),
|
||||
fs = require('fs'),
|
||||
escape = require('escape-html');
|
||||
|
||||
express().get('/list-directory', function(req, res) {
|
||||
fs.readdir('/public', function (error, fileNames) {
|
||||
var list = '<ul>';
|
||||
fileNames.forEach(fileName => {
|
||||
// GOOD: escaped `fileName` can not contain HTML elements
|
||||
list += '<li>' + escape(fileName) '</li>';
|
||||
});
|
||||
list += '</ul>'
|
||||
res.send(list);
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id js/remote-property-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-250
|
||||
|
||||
@@ -38,6 +38,7 @@ import semmle.javascript.Regexp
|
||||
import semmle.javascript.SSA
|
||||
import semmle.javascript.StandardLibrary
|
||||
import semmle.javascript.Stmt
|
||||
import semmle.javascript.StringConcatenation
|
||||
import semmle.javascript.Templates
|
||||
import semmle.javascript.Tokens
|
||||
import semmle.javascript.TypeScript
|
||||
@@ -59,6 +60,7 @@ import semmle.javascript.frameworks.Credentials
|
||||
import semmle.javascript.frameworks.CryptoLibraries
|
||||
import semmle.javascript.frameworks.DigitalOcean
|
||||
import semmle.javascript.frameworks.Electron
|
||||
import semmle.javascript.frameworks.Files
|
||||
import semmle.javascript.frameworks.jQuery
|
||||
import semmle.javascript.frameworks.LodashUnderscore
|
||||
import semmle.javascript.frameworks.Logging
|
||||
|
||||
@@ -29,6 +29,13 @@ abstract class FileSystemAccess extends DataFlow::Node {
|
||||
abstract DataFlow::Node getAPathArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that contains a file name or an array of file names from the local file system.
|
||||
*/
|
||||
abstract class FileNameSource extends DataFlow::Node {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that performs a database access.
|
||||
*/
|
||||
|
||||
80
javascript/ql/src/semmle/javascript/StringConcatenation.qll
Normal file
80
javascript/ql/src/semmle/javascript/StringConcatenation.qll
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Provides predicates for analyzing string concatenations and their operands.
|
||||
*/
|
||||
import javascript
|
||||
|
||||
module StringConcatenation {
|
||||
/** Gets a data flow node referring to the result of the given concatenation. */
|
||||
private DataFlow::Node getAssignAddResult(AssignAddExpr expr) {
|
||||
result = expr.flow()
|
||||
or
|
||||
exists (SsaExplicitDefinition def | def.getDef() = expr |
|
||||
result = DataFlow::valueNode(def.getVariable().getAUse()))
|
||||
}
|
||||
|
||||
/** Gets the `n`th operand to the string concatenation defining `node`. */
|
||||
DataFlow::Node getOperand(DataFlow::Node node, int n) {
|
||||
exists (AddExpr add | node = add.flow() |
|
||||
n = 0 and result = add.getLeftOperand().flow()
|
||||
or
|
||||
n = 1 and result = add.getRightOperand().flow())
|
||||
or
|
||||
exists (TemplateLiteral template | node = template.flow() |
|
||||
result = template.getElement(n).flow() and
|
||||
not exists (TaggedTemplateExpr tag | template = tag.getTemplate()))
|
||||
or
|
||||
exists (AssignAddExpr assign | node = getAssignAddResult(assign) |
|
||||
n = 0 and result = assign.getLhs().flow()
|
||||
or
|
||||
n = 1 and result = assign.getRhs().flow())
|
||||
or
|
||||
exists (DataFlow::ArrayCreationNode array, DataFlow::MethodCallNode call |
|
||||
call = array.getAMethodCall("join") and
|
||||
call.getArgument(0).mayHaveStringValue("") and
|
||||
(
|
||||
// step from array element to array
|
||||
result = array.getElement(n) and
|
||||
node = array
|
||||
or
|
||||
// step from array to join call
|
||||
node = call and
|
||||
result = array and
|
||||
n = 0
|
||||
))
|
||||
}
|
||||
|
||||
/** Gets an operand to the string concatenation defining `node`. */
|
||||
DataFlow::Node getAnOperand(DataFlow::Node node) {
|
||||
result = getOperand(node, _)
|
||||
}
|
||||
|
||||
/** Gets the number of operands to the given concatenation. */
|
||||
int getNumOperand(DataFlow::Node node) {
|
||||
result = strictcount(getAnOperand(node))
|
||||
}
|
||||
|
||||
/** Gets the first operand to the string concatenation defining `node`. */
|
||||
DataFlow::Node getFirstOperand(DataFlow::Node node) {
|
||||
result = getOperand(node, 0)
|
||||
}
|
||||
|
||||
/** Gets the last operand to the string concatenation defining `node`. */
|
||||
DataFlow::Node getLastOperand(DataFlow::Node node) {
|
||||
result = getOperand(node, getNumOperand(node) - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `src` flows to `dst` through the `n`th operand of the given concatenation operator.
|
||||
*/
|
||||
predicate taintStep(DataFlow::Node src, DataFlow::Node dst, DataFlow::Node operator, int n) {
|
||||
src = getOperand(dst, n) and
|
||||
operator = dst
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a taint step from `src` to `dst` through string concatenation.
|
||||
*/
|
||||
predicate taintStep(DataFlow::Node src, DataFlow::Node dst) {
|
||||
taintStep(src, dst, _, _)
|
||||
}
|
||||
}
|
||||
@@ -304,6 +304,47 @@ class ArrayLiteralNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode
|
||||
|
||||
}
|
||||
|
||||
/** A data flow node corresponding to a `new Array()` or `Array()` invocation. */
|
||||
class ArrayConstructorInvokeNode extends DataFlow::InvokeNode {
|
||||
ArrayConstructorInvokeNode() {
|
||||
getCallee() = DataFlow::globalVarRef("Array")
|
||||
}
|
||||
|
||||
/** Gets the `i`th initial element of this array, if one is provided. */
|
||||
DataFlow::ValueNode getElement(int i) {
|
||||
getNumArgument() > 1 and // A single-argument invocation specifies the array length, not an element.
|
||||
result = getArgument(i)
|
||||
}
|
||||
|
||||
/** Gets an initial element of this array, if one is provided. */
|
||||
DataFlow::ValueNode getAnElement() {
|
||||
getNumArgument() > 1 and
|
||||
result = getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node corresponding to the creation or a new array, either through an array literal
|
||||
* or an invocation of the `Array` constructor.
|
||||
*/
|
||||
class ArrayCreationNode extends DataFlow::ValueNode, DataFlow::DefaultSourceNode {
|
||||
ArrayCreationNode() {
|
||||
this instanceof ArrayLiteralNode or
|
||||
this instanceof ArrayConstructorInvokeNode
|
||||
}
|
||||
|
||||
/** Gets the `i`th initial element of this array, if one is provided. */
|
||||
DataFlow::ValueNode getElement(int i) {
|
||||
result = this.(ArrayLiteralNode).getElement(i) or
|
||||
result = this.(ArrayConstructorInvokeNode).getElement(i)
|
||||
}
|
||||
|
||||
/** Gets an initial element of this array, if one if provided. */
|
||||
DataFlow::ValueNode getAnElement() {
|
||||
result = getElement(_)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node corresponding to a `default` import from a module, or a
|
||||
* (AMD or CommonJS) `require` of a module.
|
||||
|
||||
@@ -185,38 +185,15 @@ module TaintTracking {
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
(
|
||||
exists (Expr e, Expr f | e = this.asExpr() and f = pred.asExpr() |
|
||||
// arrays with tainted elements and objects with tainted property names are tainted
|
||||
e.(ArrayExpr).getAnElement() = f or
|
||||
exists (Property prop | e.(ObjectExpr).getAProperty() = prop |
|
||||
prop.isComputed() and f = prop.getNameExpr()
|
||||
)
|
||||
or
|
||||
// awaiting a tainted expression gives a tainted result
|
||||
e.(AwaitExpr).getOperand() = f
|
||||
exists (Expr e, Expr f | e = this.asExpr() and f = pred.asExpr() |
|
||||
// arrays with tainted elements and objects with tainted property names are tainted
|
||||
e.(ArrayExpr).getAnElement() = f or
|
||||
exists (Property prop | e.(ObjectExpr).getAProperty() = prop |
|
||||
prop.isComputed() and f = prop.getNameExpr()
|
||||
)
|
||||
or
|
||||
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
|
||||
// `elt` and `ary`; similar for `forEach`
|
||||
exists (MethodCallExpr m, Function f, int i, SimpleParameter p |
|
||||
(m.getMethodName() = "map" or m.getMethodName() = "forEach") and
|
||||
(i = 0 or i = 2) and
|
||||
m.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
|
||||
p = f.getParameter(i) and
|
||||
this = DataFlow::parameterNode(p) and
|
||||
pred.asExpr() = m.getReceiver()
|
||||
)
|
||||
or
|
||||
// `array.map` with tainted return value in callback
|
||||
exists (MethodCallExpr m, Function f |
|
||||
this.asExpr() = m and
|
||||
m.getMethodName() = "map" and
|
||||
m.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
|
||||
pred = f.getAReturnedExpr().flow())
|
||||
or
|
||||
// `array.push(e)`: if `e` is tainted, then so is `array`
|
||||
succ.(DataFlow::SourceNode).getAMethodCall("push").getAnArgument() = pred
|
||||
// awaiting a tainted expression gives a tainted result
|
||||
e.(AwaitExpr).getOperand() = f
|
||||
)
|
||||
or
|
||||
// reading from a tainted object yields a tainted result
|
||||
@@ -233,6 +210,61 @@ module TaintTracking {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge caused by the builtin array functions.
|
||||
*/
|
||||
private class ArrayFunctionTaintStep extends AdditionalTaintStep {
|
||||
DataFlow::CallNode call;
|
||||
|
||||
ArrayFunctionTaintStep() {
|
||||
this = call
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
|
||||
// `elt` and `ary`; similar for `forEach`
|
||||
exists (string name, Function f, int i |
|
||||
(name = "map" or name = "forEach") and
|
||||
(i = 0 or i = 2) and
|
||||
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
|
||||
pred.(DataFlow::SourceNode).getAMethodCall(name) = call and
|
||||
succ = DataFlow::parameterNode(f.getParameter(i))
|
||||
)
|
||||
or
|
||||
// `array.map` with tainted return value in callback
|
||||
exists (DataFlow::FunctionNode f |
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
|
||||
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
|
||||
pred = f.getAReturn() and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
|
||||
exists (string name |
|
||||
name = "push" or
|
||||
name = "unshift" |
|
||||
pred = call.getAnArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
exists (string name |
|
||||
name = "pop" or
|
||||
name = "shift" or
|
||||
name = "slice" or
|
||||
name = "splice" |
|
||||
call.(DataFlow::MethodCallNode).calls(pred, name) and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
|
||||
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
|
||||
pred = call.getAnArgument() and
|
||||
succ = call
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge for assignments of the form `o[k] = v`, where
|
||||
* `k` is not a constant and `o` refers to some object literal; in this case, we consider
|
||||
@@ -326,19 +358,8 @@ module TaintTracking {
|
||||
*/
|
||||
class StringConcatenationTaintStep extends AdditionalTaintStep, DataFlow::ValueNode {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
(
|
||||
// addition propagates taint
|
||||
astNode.(AddExpr).getAnOperand() = pred.asExpr() or
|
||||
astNode.(AssignAddExpr).getAChildExpr() = pred.asExpr() or
|
||||
exists (SsaExplicitDefinition ssa |
|
||||
astNode = ssa.getVariable().getAUse() and
|
||||
pred.asExpr().(AssignAddExpr) = ssa.getDef()
|
||||
)
|
||||
or
|
||||
// templating propagates taint
|
||||
astNode.(TemplateLiteral).getAnElement() = pred.asExpr()
|
||||
)
|
||||
succ = this and
|
||||
StringConcatenation::taintStep(pred, succ)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
103
javascript/ql/src/semmle/javascript/frameworks/Files.qll
Normal file
103
javascript/ql/src/semmle/javascript/frameworks/Files.qll
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Provides classes for working with file system libraries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A file name from the `walk-sync` library.
|
||||
*/
|
||||
private class WalkSyncFileNameSource extends FileNameSource {
|
||||
|
||||
WalkSyncFileNameSource() {
|
||||
// `require('walkSync')()`
|
||||
this = DataFlow::moduleImport("walkSync").getACall()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A file name or an array of file names from the `walk` library.
|
||||
*/
|
||||
private class WalkFileNameSource extends FileNameSource {
|
||||
|
||||
WalkFileNameSource() {
|
||||
// `stats.name` in `require('walk').walk(_).on(_, (_, stats) => stats.name)`
|
||||
exists (DataFlow::FunctionNode callback |
|
||||
callback = DataFlow::moduleMember("walk", "walk").getACall().getAMethodCall("on").getCallback(1) |
|
||||
this = callback.getParameter(1).getAPropertyRead("name")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A file name or an array of file names from the `glob` library.
|
||||
*/
|
||||
private class GlobFileNameSource extends FileNameSource {
|
||||
|
||||
GlobFileNameSource() {
|
||||
exists (string moduleName |
|
||||
moduleName = "glob" |
|
||||
// `require('glob').sync(_)`
|
||||
this = DataFlow::moduleMember(moduleName, "sync").getACall()
|
||||
or
|
||||
// `name` in `require('glob')(_, (e, name) => ...)`
|
||||
this = DataFlow::moduleImport(moduleName).getACall().getCallback([1..2]).getParameter(1)
|
||||
or
|
||||
exists (DataFlow::NewNode instance |
|
||||
instance = DataFlow::moduleMember(moduleName, "Glob").getAnInstantiation() |
|
||||
// `name` in `new require('glob').Glob(_, (e, name) => ...)`
|
||||
this = instance.getCallback([1..2]).getParameter(1) or
|
||||
// `new require('glob').Glob(_).found`
|
||||
this = instance.getAPropertyRead("found")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A file name or an array of file names from the `globby` library.
|
||||
*/
|
||||
private class GlobbyFileNameSource extends FileNameSource {
|
||||
|
||||
GlobbyFileNameSource() {
|
||||
exists (string moduleName |
|
||||
moduleName = "globby" |
|
||||
// `require('globby').sync(_)`
|
||||
this = DataFlow::moduleMember(moduleName, "sync").getACall()
|
||||
or
|
||||
// `files` in `require('globby')(_).then(files => ...)`
|
||||
this = DataFlow::moduleImport(moduleName).getACall().getAMethodCall("then").getCallback(0).getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A file name or an array of file names from the `fast-glob` library.
|
||||
*/
|
||||
private class FastGlobFileNameSource extends FileNameSource {
|
||||
|
||||
FastGlobFileNameSource() {
|
||||
exists (string moduleName |
|
||||
moduleName = "fast-glob" |
|
||||
// `require('fast-glob').sync(_)`
|
||||
this = DataFlow::moduleMember(moduleName, "sync").getACall()
|
||||
or
|
||||
exists (DataFlow::SourceNode f |
|
||||
f = DataFlow::moduleImport(moduleName)
|
||||
or
|
||||
f = DataFlow::moduleMember(moduleName, "async") |
|
||||
// `files` in `require('fast-glob')(_).then(files => ...)` and
|
||||
// `files` in `require('fast-glob').async(_).then(files => ...)`
|
||||
this = f.getACall().getAMethodCall("then").getCallback(0).getParameter(0)
|
||||
)
|
||||
or
|
||||
// `file` in `require('fast-glob').stream(_).on(_, file => ...)`
|
||||
this = DataFlow::moduleMember(moduleName, "stream").getACall().getAMethodCall("on").getCallback(1).getParameter(0)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -336,17 +336,26 @@ module NodeJSLib {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A member `member` from module `fs` or its drop-in replacements `graceful-fs` or `fs-extra`.
|
||||
*/
|
||||
private DataFlow::SourceNode fsModuleMember(string member) {
|
||||
exists (string moduleName |
|
||||
moduleName = "fs" or
|
||||
moduleName = "graceful-fs" or
|
||||
moduleName = "fs-extra" |
|
||||
result = DataFlow::moduleMember(moduleName, member)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method from module `fs` or `graceful-fs`.
|
||||
* A call to a method from module `fs`, `graceful-fs` or `fs-extra`.
|
||||
*/
|
||||
private class NodeJSFileSystemAccess extends FileSystemAccess, DataFlow::CallNode {
|
||||
string methodName;
|
||||
|
||||
NodeJSFileSystemAccess() {
|
||||
exists (string moduleName | this = DataFlow::moduleMember(moduleName, methodName).getACall() |
|
||||
moduleName = "fs" or moduleName = "graceful-fs"
|
||||
)
|
||||
this = fsModuleMember(methodName).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() {
|
||||
@@ -356,6 +365,22 @@ module NodeJSLib {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that contains a file name or an array of file names from the local file system.
|
||||
*/
|
||||
private class NodeJSFileNameSource extends FileNameSource {
|
||||
|
||||
NodeJSFileNameSource() {
|
||||
exists (string name |
|
||||
name = "readdir" or
|
||||
name = "realpath" |
|
||||
this = fsModuleMember(name).getACall().getCallback([1..2]).getParameter(1) or
|
||||
this = fsModuleMember(name + "Sync").getACall()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method from module `child_process`.
|
||||
*/
|
||||
|
||||
@@ -77,9 +77,9 @@ module DomBasedXss {
|
||||
or
|
||||
// or it doesn't start with something other than `<`, and so at least
|
||||
// _may_ be interpreted as HTML
|
||||
not exists (Expr prefix, string strval |
|
||||
not exists (DataFlow::Node prefix, string strval |
|
||||
isPrefixOfJQueryHtmlString(astNode, prefix) and
|
||||
strval = prefix.getStringValue() and
|
||||
strval = prefix.asExpr().getStringValue() and
|
||||
not strval.regexpMatch("\\s*<.*")
|
||||
)
|
||||
)
|
||||
@@ -93,13 +93,14 @@ module DomBasedXss {
|
||||
* Holds if `prefix` is a prefix of `htmlString`, which may be intepreted as
|
||||
* HTML by a jQuery method.
|
||||
*/
|
||||
private predicate isPrefixOfJQueryHtmlString(Expr htmlString, Expr prefix) {
|
||||
private predicate isPrefixOfJQueryHtmlString(Expr htmlString, DataFlow::Node prefix) {
|
||||
any(JQueryMethodCall call).interpretsArgumentAsHtml(htmlString) and
|
||||
prefix = htmlString
|
||||
prefix = htmlString.flow()
|
||||
or
|
||||
exists (Expr pred | isPrefixOfJQueryHtmlString(htmlString, pred) |
|
||||
prefix = pred.(AddExpr).getLeftOperand() or
|
||||
prefix = pred.(ParExpr).getExpression()
|
||||
exists (DataFlow::Node pred | isPrefixOfJQueryHtmlString(htmlString, pred) |
|
||||
prefix = StringConcatenation::getFirstOperand(pred)
|
||||
or
|
||||
prefix = pred.getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,33 +34,23 @@ module ServerSideUrlRedirect {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the left operand of `nd` if it is a concatenation.
|
||||
*/
|
||||
private DataFlow::Node getPrefixOperand(DataFlow::Node nd) {
|
||||
exists (Expr e | e instanceof AddExpr or e instanceof AssignAddExpr |
|
||||
nd = DataFlow::valueNode(e) and
|
||||
result = DataFlow::valueNode(e.getChildExpr(0))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is transitively reachable from `nd` along prefix predecessor edges.
|
||||
*/
|
||||
private DataFlow::Node prefixCandidate(Sink sink) {
|
||||
result = sink or
|
||||
result = getPrefixOperand(prefixCandidate(sink)) or
|
||||
result = prefixCandidate(sink).getAPredecessor()
|
||||
result = prefixCandidate(sink).getAPredecessor() or
|
||||
result = StringConcatenation::getFirstOperand(prefixCandidate(sink))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets an expression that may end up being a prefix of the string concatenation `nd`.
|
||||
*/
|
||||
private Expr getAPrefix(Sink sink) {
|
||||
exists (DataFlow::Node prefix |
|
||||
prefix = prefixCandidate(sink) and
|
||||
not exists(getPrefixOperand(prefix)) and
|
||||
not exists(StringConcatenation::getFirstOperand(prefix)) and
|
||||
not exists(prefix.getAPredecessor()) and
|
||||
result = prefix.asExpr()
|
||||
)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about stored
|
||||
* cross-site scripting vulnerabilities.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.RemoteFlowSources
|
||||
import semmle.javascript.security.dataflow.ReflectedXss as ReflectedXss
|
||||
import semmle.javascript.security.dataflow.DomBasedXss as DomBasedXss
|
||||
|
||||
module StoredXss {
|
||||
/**
|
||||
* A data flow source for XSS vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for XSS vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for XSS vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about XSS.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "StoredXss" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof Source
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof Sanitizer
|
||||
}
|
||||
}
|
||||
|
||||
/** A file name, considered as a flow source for stored XSS. */
|
||||
class FileNameSourceAsSource extends Source {
|
||||
FileNameSourceAsSource() {
|
||||
this instanceof FileNameSource
|
||||
}
|
||||
}
|
||||
|
||||
/** An ordinary XSS sink, considered as a flow sink for stored XSS. */
|
||||
class XssSinkAsSink extends Sink {
|
||||
XssSinkAsSink() {
|
||||
this instanceof ReflectedXss::ReflectedXss::Sink or
|
||||
this instanceof DomBasedXss::DomBasedXss::Sink
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,16 +11,13 @@ import javascript
|
||||
* `nd` or one of its operands, assuming that it is a concatenation.
|
||||
*/
|
||||
private predicate hasSanitizingSubstring(DataFlow::Node nd) {
|
||||
exists (Expr e | e = nd.asExpr() |
|
||||
(e instanceof AddExpr or e instanceof AssignAddExpr) and
|
||||
hasSanitizingSubstring(DataFlow::valueNode(e.getAChildExpr()))
|
||||
or
|
||||
e.getStringValue().regexpMatch(".*[?#].*")
|
||||
)
|
||||
nd.asExpr().getStringValue().regexpMatch(".*[?#].*")
|
||||
or
|
||||
nd.isIncomplete(_)
|
||||
hasSanitizingSubstring(StringConcatenation::getAnOperand(nd))
|
||||
or
|
||||
hasSanitizingSubstring(nd.getAPredecessor())
|
||||
or
|
||||
nd.isIncomplete(_)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,17 +27,7 @@ private predicate hasSanitizingSubstring(DataFlow::Node nd) {
|
||||
* This is considered as a sanitizing edge for the URL redirection queries.
|
||||
*/
|
||||
predicate sanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sink) {
|
||||
exists (AddExpr add, DataFlow::Node left |
|
||||
source.asExpr() = add.getRightOperand() and
|
||||
sink.asExpr() = add and
|
||||
left.asExpr() = add.getLeftOperand() and
|
||||
hasSanitizingSubstring(left)
|
||||
)
|
||||
or
|
||||
exists (TemplateLiteral tl, int i, DataFlow::Node elt |
|
||||
source.asExpr() = tl.getElement(i) and
|
||||
sink.asExpr() = tl and
|
||||
elt.asExpr() = tl.getElement([0..i-1]) and
|
||||
hasSanitizingSubstring(elt)
|
||||
)
|
||||
exists (DataFlow::Node operator, int n |
|
||||
StringConcatenation::taintStep(source, sink, operator, n) and
|
||||
hasSanitizingSubstring(StringConcatenation::getOperand(operator, [0..n-1])))
|
||||
}
|
||||
|
||||
2
javascript/ql/test/library-tests/Lines/.gitattributes
vendored
Normal file
2
javascript/ql/test/library-tests/Lines/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file intentionally contains a mix of different line endings
|
||||
tst1.js -text
|
||||
@@ -0,0 +1,31 @@
|
||||
| tst.js:3:3:3:12 | x += "two" |
|
||||
| tst.js:3:8:3:12 | "two" |
|
||||
| tst.js:4:3:4:3 | x |
|
||||
| tst.js:4:3:4:14 | x += "three" |
|
||||
| tst.js:5:3:5:3 | x |
|
||||
| tst.js:5:3:5:13 | x += "four" |
|
||||
| tst.js:6:10:6:10 | x |
|
||||
| tst.js:12:5:12:26 | x += "o ... + "two" |
|
||||
| tst.js:12:10:12:26 | "one" + y + "two" |
|
||||
| tst.js:12:22:12:26 | "two" |
|
||||
| tst.js:19:11:19:23 | "one" + "two" |
|
||||
| tst.js:19:19:19:23 | "two" |
|
||||
| tst.js:20:3:20:3 | x |
|
||||
| tst.js:20:3:20:25 | x += (" ... "four") |
|
||||
| tst.js:21:10:21:10 | x |
|
||||
| tst.js:21:10:21:19 | x + "five" |
|
||||
| tst.js:25:10:25:32 | ["one", ... three"] |
|
||||
| tst.js:25:10:25:41 | ["one", ... oin("") |
|
||||
| tst.js:25:18:25:22 | "two" |
|
||||
| tst.js:29:10:29:37 | Array(" ... three") |
|
||||
| tst.js:29:10:29:46 | Array(" ... oin("") |
|
||||
| tst.js:29:23:29:27 | "two" |
|
||||
| tst.js:33:10:33:41 | new Arr ... three") |
|
||||
| tst.js:33:10:33:50 | new Arr ... oin("") |
|
||||
| tst.js:33:27:33:31 | "two" |
|
||||
| tst.js:38:11:38:15 | "two" |
|
||||
| tst.js:46:23:46:27 | "two" |
|
||||
| tst.js:53:10:53:34 | `one ${ ... three` |
|
||||
| tst.js:53:19:53:23 | two |
|
||||
| tst.js:71:14:71:18 | "two" |
|
||||
| tst.js:77:23:77:27 | "two" |
|
||||
@@ -0,0 +1,15 @@
|
||||
import javascript
|
||||
|
||||
// Select all expressions whose string value contains the word "two"
|
||||
|
||||
predicate containsTwo(DataFlow::Node node) {
|
||||
node.asExpr().getStringValue().regexpMatch(".*two.*")
|
||||
or
|
||||
containsTwo(node.getAPredecessor())
|
||||
or
|
||||
containsTwo(StringConcatenation::getAnOperand(node))
|
||||
}
|
||||
|
||||
from Expr e
|
||||
where containsTwo(e.flow())
|
||||
select e
|
||||
82
javascript/ql/test/library-tests/StringConcatenation/tst.js
Normal file
82
javascript/ql/test/library-tests/StringConcatenation/tst.js
Normal file
@@ -0,0 +1,82 @@
|
||||
function append() {
|
||||
let x = "one";
|
||||
x += "two";
|
||||
x += "three"
|
||||
x += "four"
|
||||
return x;
|
||||
}
|
||||
|
||||
function appendClosure(ys) {
|
||||
let x = "first";
|
||||
ys.forEach(y => {
|
||||
x += "one" + y + "two";
|
||||
});
|
||||
x += "last";
|
||||
return x;
|
||||
}
|
||||
|
||||
function appendMixed() {
|
||||
let x = "one" + "two";
|
||||
x += ("three" + "four");
|
||||
return x + "five";
|
||||
}
|
||||
|
||||
function joinArrayLiteral() {
|
||||
return ["one", "two", "three"].join("");
|
||||
}
|
||||
|
||||
function joinArrayCall() {
|
||||
return Array("one", "two", "three").join("");
|
||||
}
|
||||
|
||||
function joinArrayNewCall() {
|
||||
return new Array("one", "two", "three").join("");
|
||||
}
|
||||
|
||||
function push() {
|
||||
let xs = ["one"];
|
||||
xs.push("two");
|
||||
xs.push("three", "four");
|
||||
return xs.join("");
|
||||
}
|
||||
|
||||
function pushClosure(ys) {
|
||||
let xs = ["first"];
|
||||
ys.forEach(y => {
|
||||
xs.push("one", y, "two");
|
||||
});
|
||||
xs.push("last");
|
||||
return xs.join("");
|
||||
}
|
||||
|
||||
function template(x) {
|
||||
return `one ${x} two ${x} three`;
|
||||
}
|
||||
|
||||
function taggedTemplate(mid) {
|
||||
return someTag`first ${mid} last`;
|
||||
}
|
||||
|
||||
function templateRepeated(x) {
|
||||
return `first ${x}${x}${x} last`;
|
||||
}
|
||||
|
||||
function makeArray() {
|
||||
return [];
|
||||
}
|
||||
|
||||
function pushNoLocalCreation() {
|
||||
let array = makeArray();
|
||||
array.push("one");
|
||||
array.push("two");
|
||||
array.push("three");
|
||||
return array.join("");
|
||||
}
|
||||
|
||||
function joinInClosure() {
|
||||
let array = ["one", "two", "three"];
|
||||
function f() {
|
||||
return array.join();
|
||||
}
|
||||
return f();
|
||||
}
|
||||
@@ -3,3 +3,15 @@
|
||||
| tst.js:2:13:2:20 | source() | tst.js:14:10:14:17 | x.sort() |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:17:10:17:10 | a |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:19:10:19:10 | a |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:23:10:23:10 | b |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:25:10:25:16 | x.pop() |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:26:10:26:18 | x.shift() |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:27:10:27:18 | x.slice() |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:28:10:28:19 | x.splice() |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:30:10:30:22 | Array.from(x) |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:33:14:33:16 | elt |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:35:14:35:16 | ary |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:39:14:39:16 | elt |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:41:14:41:16 | ary |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:44:10:44:30 | innocen ... ) => x) |
|
||||
| tst.js:2:13:2:20 | source() | tst.js:45:10:45:24 | x.map(x2 => x2) |
|
||||
|
||||
@@ -18,4 +18,30 @@ function test() {
|
||||
a.push(x);
|
||||
sink(a); // NOT OK
|
||||
|
||||
var b = [];
|
||||
b.unshift(x);
|
||||
sink(b); // NOT OK
|
||||
|
||||
sink(x.pop()); // NOT OK
|
||||
sink(x.shift()); // NOT OK
|
||||
sink(x.slice()); // NOT OK
|
||||
sink(x.splice()); // NOT OK
|
||||
|
||||
sink(Array.from(x)); // NOT OK
|
||||
|
||||
x.map((elt, i, ary) => {
|
||||
sink(elt); // NOT OK
|
||||
sink(i); // OK
|
||||
sink(ary); // NOT OK
|
||||
});
|
||||
|
||||
x.forEach((elt, i, ary) => {
|
||||
sink(elt); // NOT OK
|
||||
sink(i); // OK
|
||||
sink(ary); // NOT OK
|
||||
});
|
||||
|
||||
sink(innocent.map(() => x)); // NOT OK
|
||||
sink(x.map(x2 => x2)); // NOT OK
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
| tst-file-names.js:7:1:7:10 | walkSync() |
|
||||
| tst-file-names.js:9:35:9:44 | stats.name |
|
||||
| tst-file-names.js:11:1:11:12 | glob.sync(_) |
|
||||
| tst-file-names.js:13:13:13:16 | name |
|
||||
| tst-file-names.js:15:22:15:25 | name |
|
||||
| tst-file-names.js:17:1:17:22 | new glo ... ).found |
|
||||
| tst-file-names.js:19:1:19:14 | globby.sync(_) |
|
||||
| tst-file-names.js:21:16:21:20 | files |
|
||||
| tst-file-names.js:23:1:23:16 | fastGlob.sync(_) |
|
||||
| tst-file-names.js:25:18:25:22 | files |
|
||||
| tst-file-names.js:27:24:27:28 | files |
|
||||
| tst-file-names.js:29:27:29:30 | file |
|
||||
@@ -0,0 +1,3 @@
|
||||
import javascript
|
||||
|
||||
select any(FileNameSource s)
|
||||
@@ -0,0 +1,29 @@
|
||||
let walkSync = require('walkSync'),
|
||||
walk = require('walk'),
|
||||
glob = require('glob'),
|
||||
globby = require('globby'),
|
||||
fastGlob = require('fast-glob');
|
||||
|
||||
walkSync();
|
||||
|
||||
walk.walk(_).on(_, (_, stats) => stats.name); // XXX
|
||||
|
||||
glob.sync(_);
|
||||
|
||||
glob(_, (e, name) => name);
|
||||
|
||||
new glob.Glob(_, (e, name) => name);
|
||||
|
||||
new glob.Glob(_).found;
|
||||
|
||||
globby.sync(_);
|
||||
|
||||
globby(_).then(files => files)
|
||||
|
||||
fastGlob.sync(_);
|
||||
|
||||
fastGlob(_).then(files => files);
|
||||
|
||||
fastGlob.async(_).then(files => files);
|
||||
|
||||
fastGlob.stream(_).on(_, file => file); // XXX
|
||||
3
javascript/ql/test/query-tests/AlertSuppression/.gitattributes
vendored
Normal file
3
javascript/ql/test/query-tests/AlertSuppression/.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These files intentionally contain CRLF line endings.
|
||||
tstWindows.html eol=crlf
|
||||
tstWindows.js eol=crlf
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head><title>Title</title></title>
|
||||
<body>
|
||||
<div id="duplicate-id"/>
|
||||
<div id="duplicate-id"/> <!-- lgtm -->
|
||||
</body>
|
||||
</html>
|
||||
<html>
|
||||
<head><title>Title</title></title>
|
||||
<body>
|
||||
<div id="duplicate-id"/>
|
||||
<div id="duplicate-id"/> <!-- lgtm -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
debugger; // lgtm
|
||||
// lgtm[js/debugger-statement]
|
||||
// lgtm[js/debugger-statement, js/invocation-of-non-function]
|
||||
// lgtm[@tag:nullness]
|
||||
// lgtm[@tag:nullness,js/debugger-statement]
|
||||
// lgtm[@expires:2017-06-11]
|
||||
// lgtm[js/invocation-of-non-function] because I know better than lgtm
|
||||
// lgtm: blah blah
|
||||
// lgtm blah blah #falsepositive
|
||||
//lgtm [js/invocation-of-non-function]
|
||||
/* lgtm */
|
||||
// lgtm[]
|
||||
// lgtmfoo
|
||||
//lgtm
|
||||
// lgtm
|
||||
// lgtm [js/debugger-statement]
|
||||
// foolgtm[js/debugger-statement]
|
||||
// foolgtm
|
||||
// foo; lgtm
|
||||
// foo; lgtm[js/debugger-statement]
|
||||
// foo lgtm
|
||||
// foo lgtm[js/debugger-statement]
|
||||
// foo lgtm bar
|
||||
// foo lgtm[js/debugger-statement] bar
|
||||
// LGTM!
|
||||
// LGTM[js/debugger-statement]
|
||||
// lgtm[js/debugger-statement] and lgtm[js/invocation-of-non-function]
|
||||
// lgtm[js/debugger-statement]; lgtm
|
||||
debugger; // lgtm
|
||||
// lgtm[js/debugger-statement]
|
||||
// lgtm[js/debugger-statement, js/invocation-of-non-function]
|
||||
// lgtm[@tag:nullness]
|
||||
// lgtm[@tag:nullness,js/debugger-statement]
|
||||
// lgtm[@expires:2017-06-11]
|
||||
// lgtm[js/invocation-of-non-function] because I know better than lgtm
|
||||
// lgtm: blah blah
|
||||
// lgtm blah blah #falsepositive
|
||||
//lgtm [js/invocation-of-non-function]
|
||||
/* lgtm */
|
||||
// lgtm[]
|
||||
// lgtmfoo
|
||||
//lgtm
|
||||
// lgtm
|
||||
// lgtm [js/debugger-statement]
|
||||
// foolgtm[js/debugger-statement]
|
||||
// foolgtm
|
||||
// foo; lgtm
|
||||
// foo; lgtm[js/debugger-statement]
|
||||
// foo lgtm
|
||||
// foo lgtm[js/debugger-statement]
|
||||
// foo lgtm bar
|
||||
// foo lgtm[js/debugger-statement] bar
|
||||
// LGTM!
|
||||
// LGTM[js/debugger-statement]
|
||||
// lgtm[js/debugger-statement] and lgtm[js/invocation-of-non-function]
|
||||
// lgtm[js/debugger-statement]; lgtm
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | The `nodeIntegrationInWorker` feature has been enabled. |
|
||||
| EnablingNodeIntegration.js:5:28:11:9 | {\\n ... } | The `nodeIntegration` feature has been enabled. |
|
||||
| EnablingNodeIntegration.js:15:22:20:9 | {\\n ... } | The `nodeIntegration` feature is enabled by default. |
|
||||
| EnablingNodeIntegration.js:23:16:27:9 | { // NO ... } | The `nodeIntegration` feature is enabled by default. |
|
||||
| EnablingNodeIntegration.js:49:74:49:96 | {nodeIn ... : true} | The `nodeIntegration` feature has been enabled. |
|
||||
@@ -0,0 +1,52 @@
|
||||
const {BrowserWindow} = require('electron')
|
||||
|
||||
function test() {
|
||||
var unsafe_1 = { // NOT OK, both enabled
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
nodeIntegrationInWorker: true,
|
||||
plugins: true,
|
||||
webSecurity: true,
|
||||
sandbox: true
|
||||
}
|
||||
};
|
||||
|
||||
var options_1 = { // NOT OK, `nodeIntegrationInWorker` enabled
|
||||
webPreferences: {
|
||||
plugins: true,
|
||||
nodeIntegrationInWorker: false,
|
||||
webSecurity: true,
|
||||
sandbox: true
|
||||
}
|
||||
};
|
||||
|
||||
var pref = { // NOT OK, implicitly enabled
|
||||
plugins: true,
|
||||
webSecurity: true,
|
||||
sandbox: true
|
||||
};
|
||||
|
||||
var options_2 = { // NOT OK, implicitly enabled
|
||||
webPreferences: pref,
|
||||
show: true,
|
||||
frame: true,
|
||||
minWidth: 300,
|
||||
minHeight: 300
|
||||
};
|
||||
|
||||
var safe_used = { // NOT OK, explicitly disabled
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
plugins: true,
|
||||
webSecurity: true,
|
||||
sandbox: true
|
||||
}
|
||||
};
|
||||
|
||||
var w1 = new BrowserWindow(unsafe_1);
|
||||
var w2 = new BrowserWindow(options_1);
|
||||
var w3 = new BrowserWindow(safe_used);
|
||||
var w4 = new BrowserWindow({width: 800, height: 600, webPreferences: {nodeIntegration: true}}); // NOT OK, `nodeIntegration` enabled
|
||||
var w5 = new BrowserWindow(options_2);
|
||||
var w6 = new BrowserWindow(safe_used);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Electron/EnablingNodeIntegration.ql
|
||||
@@ -1,3 +1,3 @@
|
||||
| tst.js:56:18:56:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:14:9:14:12 | this | this | tst.js:13:5:15:5 | unbound ... ;\\n } | unbound1 |
|
||||
| tst.js:57:18:57:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:18:15:18:18 | this | this | tst.js:17:5:19:5 | unbound ... ;\\n } | unbound2 |
|
||||
| tst.js:58:18:58:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:22:15:22:18 | this | this | tst.js:21:5:23:5 | unbound ... ;\\n } | unbound3 |
|
||||
| tst.js:27:18:27:40 | onClick ... bound1} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:56:9:56:12 | this | this | tst.js:55:5:57:5 | unbound ... ;\\n } | unbound1 |
|
||||
| tst.js:28:18:28:40 | onClick ... bound2} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:60:15:60:18 | this | this | tst.js:59:5:61:5 | unbound ... ;\\n } | unbound2 |
|
||||
| tst.js:29:18:29:35 | onClick={unbound3} | The receiver of this event handler call is unbound, `$@` will be `undefined` in the call to $@ | tst.js:64:15:64:18 | this | this | tst.js:63:5:65:5 | unbound ... ;\\n } | unbound3 |
|
||||
|
||||
@@ -1,6 +1,46 @@
|
||||
import React from 'react';
|
||||
import autoBind from 'auto-bind';
|
||||
|
||||
class Component0 extends React.Component {
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<div onClick={this.bound_throughAutoBind}/> // OK
|
||||
</div>
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
bound_throughAutoBind() {
|
||||
this.setState({ });
|
||||
}
|
||||
}
|
||||
|
||||
class Component1 extends React.Component {
|
||||
|
||||
render() {
|
||||
var unbound3 = this.unbound3;
|
||||
return <div>
|
||||
<div onClick={this.unbound1}/> // NOT OK
|
||||
<div onClick={this.unbound2}/> // NOT OK
|
||||
<div onClick={unbound3}/> // NOT OK
|
||||
<div onClick={this.bound_throughBindInConstructor}/> // OK
|
||||
<div onClick={this.bound_throughDeclaration}/> // OK
|
||||
<div onClick={this.unbound_butNoThis}/> // OK
|
||||
<div onClick={this.unbound_butNoThis2}/> // OK
|
||||
<div onClick={(e) => this.unbound_butInvokedSafely(e)}/> // OK
|
||||
<div onClick={this.bound_throughBindInMethod}/> // OK
|
||||
<div onClick={this.bound_throughNonSyntacticBindInConstructor}/> // OK
|
||||
<div onClick={this.bound_throughBindAllInConstructor1}/> // OK
|
||||
<div onClick={this.bound_throughBindAllInConstructor2}/> // OK
|
||||
<div onClick={this.bound_throughDecorator_autobind}/> // OK
|
||||
<div onClick={this.bound_throughDecorator_actionBound}/> // OK
|
||||
</div>
|
||||
}
|
||||
|
||||
class Component extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.bound_throughBindInConstructor = this.bound_throughBindInConstructor.bind(this);
|
||||
@@ -8,6 +48,8 @@ class Component extends React.Component {
|
||||
var cmp = this;
|
||||
var bound = (cmp.bound_throughNonSyntacticBindInConstructor.bind(this));
|
||||
(cmp).bound_throughNonSyntacticBindInConstructor = bound;
|
||||
_.bindAll(this, 'bound_throughBindAllInConstructor1');
|
||||
_.bindAll(this, ['bound_throughBindAllInConstructor2']);
|
||||
}
|
||||
|
||||
unbound1() {
|
||||
@@ -50,22 +92,6 @@ class Component extends React.Component {
|
||||
this.setState({ });
|
||||
}
|
||||
|
||||
render() {
|
||||
var unbound3 = this.unbound3;
|
||||
return <div>
|
||||
<div onClick={this.unbound1}/> // NOT OK
|
||||
<div onClick={this.unbound2}/> // NOT OK
|
||||
<div onClick={unbound3}/> // NOT OK
|
||||
<div onClick={this.bound_throughBindInConstructor}/> // OK
|
||||
<div onClick={this.bound_throughDeclaration}/> // OK
|
||||
<div onClick={this.unbound_butNoThis}/> // OK
|
||||
<div onClick={this.unbound_butNoThis2}/> // OK
|
||||
<div onClick={(e) => this.unbound_butInvokedSafely(e)}/> // OK
|
||||
<div onClick={this.bound_throughBindInMethod}/> // OK
|
||||
<div onClick={this.bound_throughNonSyntacticBindInConstructor}/> // OK
|
||||
</div>
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.bound_throughBindInMethod = this.bound_throughBindInMethod.bind(this);
|
||||
}
|
||||
@@ -74,6 +100,24 @@ class Component extends React.Component {
|
||||
this.setState({ });
|
||||
}
|
||||
|
||||
bound_throughBindAllInConstructor1() {
|
||||
this.setState({ });
|
||||
}
|
||||
|
||||
bound_throughBindAllInConstructor2() {
|
||||
this.setState({ });
|
||||
}
|
||||
|
||||
@autobind
|
||||
bound_throughDecorator_autobind() {
|
||||
this.setState({ });
|
||||
}
|
||||
|
||||
@action.bound
|
||||
bound_throughDecorator_actionBound() {
|
||||
this.setState({ });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// semmle-extractor-options: --experimental
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| xss-through-filenames.js:8:18:8:23 | files1 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:7:43:7:48 | files1 | stored value |
|
||||
| xss-through-filenames.js:26:19:26:24 | files1 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value |
|
||||
| xss-through-filenames.js:33:19:33:24 | files2 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value |
|
||||
| xss-through-filenames.js:37:19:37:24 | files3 | Stored cross-site scripting vulnerability due to $@. | xss-through-filenames.js:25:43:25:48 | files1 | stored value |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-079/StoredXss.ql
|
||||
@@ -0,0 +1,40 @@
|
||||
var http = require('http');
|
||||
var fs = require('fs');
|
||||
|
||||
var express = require('express');
|
||||
|
||||
express().get('/', function(req, res) {
|
||||
fs.readdir("/myDir", function (error, files1) {
|
||||
res.send(files1); // NOT OK
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* The essence of a real world vulnerability.
|
||||
*/
|
||||
http.createServer(function (req, res) {
|
||||
|
||||
function format(files2) {
|
||||
var files3 = [];
|
||||
files2.sort(sort).forEach(function (file) {
|
||||
files3.push('<li>' + file + '</li>');
|
||||
});
|
||||
return files3.join('');
|
||||
}
|
||||
|
||||
fs.readdir("/myDir", function (error, files1) {
|
||||
res.write(files1); // NOT OK
|
||||
|
||||
var dirs = [];
|
||||
var files2 = [];
|
||||
files1.forEach(function (file) {
|
||||
files2.push(file);
|
||||
});
|
||||
res.write(files2); // NOT OK
|
||||
|
||||
var files3 = format(files2);
|
||||
|
||||
res.write(files3); // NOT OK
|
||||
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,7 @@
|
||||
| express.js:78:16:78:43 | `${req. ... )}/foo` | Untrusted URL redirection due to $@. | express.js:78:19:78:37 | req.param("target") | user-provided value |
|
||||
| express.js:94:18:94:23 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value |
|
||||
| express.js:101:16:101:21 | target | Untrusted URL redirection due to $@. | express.js:87:16:87:34 | req.param("target") | user-provided value |
|
||||
| express.js:122:16:122:72 | [req.qu ... oin('') | Untrusted URL redirection due to $@. | express.js:122:17:122:30 | req.query.page | user-provided value |
|
||||
| node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value |
|
||||
| node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value |
|
||||
| node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value |
|
||||
|
||||
@@ -110,3 +110,14 @@ app.get('/some/path', function(req, res) {
|
||||
else
|
||||
res.redirect(target);
|
||||
});
|
||||
|
||||
app.get('/array/join', function(req, res) {
|
||||
// GOOD: request input embedded in query string
|
||||
res.redirect(['index.html?section=', req.query.section].join(''));
|
||||
|
||||
// GOOD: request input still embedded in query string
|
||||
res.redirect(['index.html?section=', '34'].join('') + '&subsection=' + req.query.subsection);
|
||||
|
||||
// BAD: request input becomes before query string
|
||||
res.redirect([req.query.page, '?section=', req.query.section].join(''));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user