Merge branch 'master' into master

This commit is contained in:
Raul Garcia
2018-09-20 16:30:48 -07:00
committed by GitHub
85 changed files with 4546 additions and 164 deletions

View File

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

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

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

View File

@@ -1,2 +1,3 @@
/csharp/ @Semmle/cs
/java/ @Semmle/java
/javascript/ @Semmle/js

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
import implementation.aliased_ssa.PrintIR

View File

@@ -0,0 +1 @@
import implementation.aliased_ssa.gvn.ValueNumbering

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.raw.IR as IR

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -5,3 +5,4 @@ missingPhiOperand
instructionWithoutSuccessor
unnecessaryPhiInstruction
operandAcrossFunctions
instructionWithoutUniqueBlock

View File

@@ -5,3 +5,4 @@ missingPhiOperand
instructionWithoutSuccessor
unnecessaryPhiInstruction
operandAcrossFunctions
instructionWithoutUniqueBlock

View File

@@ -5,3 +5,4 @@ missingPhiOperand
instructionWithoutSuccessor
unnecessaryPhiInstruction
operandAcrossFunctions
instructionWithoutUniqueBlock

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
/**
* @kind graph
*/
import semmle.code.cpp.ir.PrintIR
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.ValueNumbering

View File

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

View File

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

View File

@@ -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()
, " ")

View File

@@ -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(), ", ")

View 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;
}

View File

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

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

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

View File

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

View File

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

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

View 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"

View 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);
});
});

View 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);
});
});

View File

@@ -5,7 +5,7 @@
*
* @kind problem
* @problem.severity warning
* @precision high
* @precision medium
* @id js/remote-property-injection
* @tags security
* external/cwe/cwe-250

View File

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

View File

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

View 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, _, _)
}
}

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
# This file intentionally contains a mix of different line endings
tst1.js -text

View File

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

View File

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

View 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();
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
import javascript
select any(FileNameSource s)

View File

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

View File

@@ -0,0 +1,3 @@
# These files intentionally contain CRLF line endings.
tstWindows.html eol=crlf
tstWindows.js eol=crlf

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
Electron/EnablingNodeIntegration.ql

View File

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

View File

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

View File

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

View File

@@ -0,0 +1 @@
Security/CWE-079/StoredXss.ql

View File

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

View File

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

View File

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