Merge branch 'main' into amammad-python-WebAppsConstatntSecretKeys

This commit is contained in:
Rasmus Wriedt Larsen
2023-08-16 15:06:08 +02:00
committed by GitHub
111 changed files with 2790 additions and 1291 deletions

View File

@@ -327,7 +327,7 @@ namespace Semmle.Autobuild.Cpp.Tests
{
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program^ Files^ ^(x86^)\Microsoft^ Visual^ Studio^ 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;

View File

@@ -1520,6 +1520,25 @@ private module Cached {
)
}
/**
* Holds if `operand.getDef() = instr`, but there exists a `StoreInstruction` that
* writes to an address that is equivalent to the value computed by `instr` in
* between `instr` and `operand`, and therefore there should not be flow from `*instr`
* to `*operand`.
*/
pragma[nomagic]
private predicate isStoredToBetween(Instruction instr, Operand operand) {
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand)) and
exists(StoreInstruction store, IRBlock block, int storeIndex, int instrIndex, int operandIndex |
store.getDestinationAddress() = instr and
block.getInstruction(storeIndex) = store and
block.getInstruction(instrIndex) = instr and
block.getInstruction(operandIndex) = operand.getUse() and
instrIndex < storeIndex and
storeIndex < operandIndex
)
}
private predicate indirectionInstructionFlow(
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
) {
@@ -1529,7 +1548,8 @@ private module Cached {
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand))
|
hasOperandAndIndex(nodeTo, operand, pragma[only_bind_into](indirectionIndex)) and
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex))
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex)) and
not isStoredToBetween(instr, operand)
)
}

View File

@@ -6,6 +6,7 @@ private import DataFlowImplCommon as DataFlowImplCommon
private import DataFlowUtil
private import semmle.code.cpp.models.interfaces.PointerWrapper
private import DataFlowPrivate
private import semmle.code.cpp.ir.ValueNumbering
/**
* Holds if `operand` is an operand that is not used by the dataflow library.
@@ -864,7 +865,7 @@ private module Cached {
* to a specific address.
*/
private predicate isCertainAddress(Operand operand) {
operand.getDef() instanceof VariableAddressInstruction
valueNumberOfOperand(operand).getAnInstruction() instanceof VariableAddressInstruction
or
operand.getType() instanceof Cpp::ReferenceType
}

View File

@@ -574,16 +574,6 @@ module RangeStage<
)
}
/** Holds if `e >= 1` as determined by sign analysis. */
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
}
/** Holds if `e <= -1` as determined by sign analysis. */
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
}
/**
* Holds if `e1 + delta` is a valid bound for `e2`.
* - `upper = true` : `e2 <= e1 + delta`
@@ -597,27 +587,6 @@ module RangeStage<
delta = D::fromInt(0) and
(upper = true or upper = false)
or
exists(SemExpr x, SemSubExpr sub |
e2 = sub and
sub.getLeftOperand() = e1 and
sub.getRightOperand() = x
|
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
not x instanceof SemConstantIntegerExpr and
if strictlyPositiveIntegralExpr(x)
then upper = true and delta = D::fromInt(-1)
else
if semPositive(x)
then upper = true and delta = D::fromInt(0)
else
if strictlyNegativeIntegralExpr(x)
then upper = false and delta = D::fromInt(1)
else
if semNegative(x)
then upper = false and delta = D::fromInt(0)
else none()
)
or
e2.(SemRemExpr).getRightOperand() = e1 and
semPositive(e1) and
delta = D::fromInt(-1) and
@@ -1137,6 +1106,23 @@ module RangeStage<
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
)
or
exists(D::Delta dLeft, D::Delta dRight, boolean fbeLeft, boolean fbeRight |
boundedSubOperandLeft(e, upper, b, dLeft, fbeLeft, origdelta, reason) and
boundedSubOperandRight(e, upper, dRight, fbeRight) and
// when `upper` is `true` we have:
// left <= b + dLeft
// right >= 0 + dRight
// left - right <= b + dLeft - (0 + dRight)
// = b + (dLeft - dRight)
// and when `upper` is `false` we have:
// left >= b + dLeft
// right <= 0 + dRight
// left - right >= b + dLeft - (0 + dRight)
// = b + (dLeft - dRight)
delta = D::fromFloat(D::toFloat(dLeft) - D::toFloat(dRight)) and
fromBackEdge = fbeLeft.booleanOr(fbeRight)
)
or
exists(
SemRemExpr rem, D::Delta d_max, D::Delta d1, D::Delta d2, boolean fbe1, boolean fbe2,
D::Delta od1, D::Delta od2, SemReason r1, SemReason r2
@@ -1201,6 +1187,37 @@ module RangeStage<
)
}
/**
* Holds if `sub = left - right` and `left <= b + delta` if `upper` is `true`
* and `left >= b + delta` is `upper` is `false`.
*/
pragma[nomagic]
private predicate boundedSubOperandLeft(
SemSubExpr sub, boolean upper, SemBound b, D::Delta delta, boolean fromBackEdge,
D::Delta origdelta, SemReason reason
) {
// `semValueFlowStep` already handles the case where one of the operands is a constant.
not semValueFlowStep(sub, _, _) and
bounded(sub.getLeftOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
}
/**
* Holds if `sub = left - right` and `right <= 0 + delta` if `upper` is `false`
* and `right >= 0 + delta` is `upper` is `true`.
*
* Note that the boolean value of `upper` is flipped compared to many other predicates in
* this file. This ensures a clean join at the call-site.
*/
pragma[nomagic]
private predicate boundedSubOperandRight(
SemSubExpr sub, boolean upper, D::Delta delta, boolean fromBackEdge
) {
// `semValueFlowStep` already handles the case where one of the operands is a constant.
not semValueFlowStep(sub, _, _) and
bounded(sub.getRightOperand(), any(SemZeroBound zb), delta, upper.booleanNot(), fromBackEdge, _,
_)
}
pragma[nomagic]
private predicate boundedRemExpr(
SemRemExpr rem, boolean upper, D::Delta delta, boolean fromBackEdge, D::Delta origdelta,

View File

@@ -96,7 +96,7 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
* but because there's a strict comparison that compares `n` against the size of the allocation this
* snippet is fine.
*/
module SizeBarrier {
private module SizeBarrier {
private module SizeBarrierConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
// The sources is the same as in the sources for the second
@@ -104,35 +104,60 @@ module SizeBarrier {
hasSize(_, source, _)
}
/**
* Holds if `small <= large + k` holds if `g` evaluates to `testIsTrue`.
*/
additional predicate isSink(
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
) {
// The sink is any "large" side of a relational comparison. i.e., the `right` expression
// in a guard such as `left < right + k`.
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
// The sink is any "large" side of a relational comparison. i.e., the `large` expression
// in a guard such as `small <= large + k`.
g.comparesLt(small.asOperand(), large.asOperand(), k + 1, true, testIsTrue)
}
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
}
private import DataFlow::Global<SizeBarrierConfig>
module SizeBarrierFlow = DataFlow::Global<SizeBarrierConfig>;
private int getAFlowStateForNode(DataFlow::Node node) {
private int getASizeAddend(DataFlow::Node node) {
exists(DataFlow::Node source |
flow(source, node) and
SizeBarrierFlow::flow(source, node) and
hasSize(_, source, result)
)
}
/**
* Holds if `small <= large + k` holds if `g` evaluates to `edge`.
*/
private predicate operandGuardChecks(
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
IRGuardCondition g, Operand small, DataFlow::Node large, int k, boolean edge
) {
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
nRight.asOperand() = right and
nLeft.asOperand() = left and
SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and
state = getAFlowStateForNode(nRight) and
k <= state
SizeBarrierFlow::flowTo(large) and
SizeBarrierConfig::isSink(DataFlow::operandNode(small), large, g, k, edge)
}
/**
* Gets an instruction `instr` that is guarded by a check such as `instr <= small + delta` where
* `small <= _ + k` and `small` is the "small side" of of a relational comparison that checks
* whether `small <= size` where `size` is the size of an allocation.
*/
Instruction getABarrierInstruction0(int delta, int k) {
exists(
IRGuardCondition g, ValueNumber value, Operand small, boolean edge, DataFlow::Node large
|
// We know:
// 1. result <= value + delta (by `bounded`)
// 2. value <= large + k (by `operandGuardChecks`).
// So:
// result <= value + delta (by 1.)
// <= large + k + delta (by 2.)
small = value.getAUse() and
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](small), large,
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
bounded(result, value.getAnInstruction(), delta) and
g.controls(result.getBlock(), edge) and
k < getASizeAddend(large)
)
}
@@ -140,13 +165,14 @@ module SizeBarrier {
* Gets an instruction that is guarded by a guard condition which ensures that
* the value of the instruction is upper-bounded by size of some allocation.
*/
bindingset[state]
pragma[inline_late]
Instruction getABarrierInstruction(int state) {
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
use = value.getAUse() and
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _,
pragma[only_bind_into](state), pragma[only_bind_into](edge)) and
result = value.getAnInstruction() and
g.controls(result.getBlock(), edge)
exists(int delta, int k |
state > k + delta and
// result <= "size of allocation" + delta + k
// < "size of allocation" + state
result = getABarrierInstruction0(delta, k)
)
}
@@ -155,14 +181,16 @@ module SizeBarrier {
* the value of the node is upper-bounded by size of some allocation.
*/
DataFlow::Node getABarrierNode(int state) {
result.asOperand() = getABarrierInstruction(state).getAUse()
exists(DataFlow::Node source, int delta, int k |
SizeBarrierFlow::flow(source, result) and
hasSize(_, source, state) and
result.asInstruction() = SizeBarrier::getABarrierInstruction0(delta, k) and
state > k + delta
// so now we have:
// result <= "size of allocation" + delta + k
// < "size of allocation" + state
)
}
/**
* Gets the block of a node that is guarded (see `getABarrierInstruction` or
* `getABarrierNode` for the definition of what it means to be guarded).
*/
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
}
private module InterestingPointerAddInstruction {

View File

@@ -66,11 +66,14 @@
* module. Since the node we are tracking is not necessarily _equal_ to the pointer-arithmetic instruction, but rather satisfies
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`, we need to account for the delta when checking if a guard is sufficiently
* strong to infer that a future dereference is safe. To do this, we check that the guard guarantees that a node `n` satisfies
* `n < node + k` where `node` is a node we know is equal to the value of the dereference source (i.e., it satisfies
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`) and `k <= deltaDerefSourceAndPai`. Combining this we have
* `n < node + k <= node + deltaDerefSourceAndPai <= pai + 2*deltaDerefSourceAndPai` (TODO: Oops. This math doesn't quite work out.
* I think this is because we need to redefine the `BarrierConfig` to start flow at the pointer-arithmetic instruction instead of
* at the dereference source. When combined with TODO above it's easy to show that this guard ensures that the dereference is safe).
* `n < node + k` where `node` is a node such that `node <= pai`. Thus, we know that any node `m` such that `m <= n + delta` where
* `delta + k <= 0` will be safe because:
* ```
* m <= n + delta
* < node + k + delta
* <= pai + k + delta
* <= pai
* ```
*/
private import cpp
@@ -82,16 +85,19 @@ private import RangeAnalysisUtil
private module InvalidPointerToDerefBarrier {
private module BarrierConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
// The sources is the same as in the sources for `InvalidPointerToDerefConfig`.
invalidPointerToDerefSource(_, _, source, _)
additional predicate isSource(DataFlow::Node source, PointerArithmeticInstruction pai) {
invalidPointerToDerefSource(_, pai, _, _) and
// source <= pai
bounded2(source.asInstruction(), pai, any(int d | d <= 0))
}
predicate isSource(DataFlow::Node source) { isSource(source, _) }
additional predicate isSink(
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
) {
// The sink is any "large" side of a relational comparison.
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
g.comparesLt(small.asOperand(), large.asOperand(), k, true, testIsTrue)
}
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
@@ -99,59 +105,82 @@ private module InvalidPointerToDerefBarrier {
private module BarrierFlow = DataFlow::Global<BarrierConfig>;
private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) {
exists(DataFlow::Node source |
BarrierFlow::flow(source, node) and
invalidPointerToDerefSource(_, _, source, result)
)
}
/**
* Holds if `g` ensures that `small < large + k` if `g` evaluates to `edge`.
*
* Additionally, it also holds that `large <= pai`. Thus, when `g` evaluates to `edge`
* it holds that `small < pai + k`.
*/
private predicate operandGuardChecks(
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
PointerArithmeticInstruction pai, IRGuardCondition g, Operand small, int k, boolean edge
) {
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
nRight.asOperand() = right and
nLeft.asOperand() = left and
BarrierConfig::isSink(nLeft, nRight, g, k, edge) and
state = getInvalidPointerToDerefSourceDelta(nRight) and
k <= state
exists(DataFlow::Node source, DataFlow::Node nSmall, DataFlow::Node nLarge |
nSmall.asOperand() = small and
BarrierConfig::isSource(source, pai) and
BarrierFlow::flow(source, nLarge) and
BarrierConfig::isSink(nSmall, nLarge, g, k, edge)
)
}
Instruction getABarrierInstruction(int state) {
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
/**
* Gets an instruction `instr` such that `instr < pai`.
*/
Instruction getABarrierInstruction(PointerArithmeticInstruction pai) {
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge, int delta, int k |
use = value.getAUse() and
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, state,
pragma[only_bind_into](edge)) and
result = value.getAnInstruction() and
g.controls(result.getBlock(), edge)
// value < pai + k
operandGuardChecks(pai, pragma[only_bind_into](g), pragma[only_bind_into](use),
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
// result <= value + delta
bounded(result, value.getAnInstruction(), delta) and
g.controls(result.getBlock(), edge) and
delta + k <= 0
// combining the above we have: result < pai + k + delta <= pai
)
}
DataFlow::Node getABarrierNode() { result.asOperand() = getABarrierInstruction(_).getAUse() }
DataFlow::Node getABarrierNode(PointerArithmeticInstruction pai) {
result.asOperand() = getABarrierInstruction(pai).getAUse()
}
pragma[nomagic]
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
/**
* Gets an address operand whose definition `instr` satisfies `instr < pai`.
*/
AddressOperand getABarrierAddressOperand(PointerArithmeticInstruction pai) {
result.getDef() = getABarrierInstruction(pai)
}
}
/**
* A configuration to track flow from a pointer-arithmetic operation found
* by `AllocToInvalidPointerConfig` to a dereference of the pointer.
*/
private module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, _, source, _) }
private module InvalidPointerToDerefConfig implements DataFlow::StateConfigSig {
class FlowState extends PointerArithmeticInstruction {
FlowState() { invalidPointerToDerefSource(_, this, _, _) }
}
predicate isSource(DataFlow::Node source, FlowState pai) {
invalidPointerToDerefSource(_, pai, source, _)
}
pragma[inline]
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _) }
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _, _) }
predicate isSink(DataFlow::Node sink, FlowState pai) { none() }
predicate isBarrier(DataFlow::Node node) {
node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true)
or
node = InvalidPointerToDerefBarrier::getABarrierNode()
}
predicate isBarrier(DataFlow::Node node, FlowState pai) {
// `node = getABarrierNode(pai)` ensures that node < pai, so this node is safe to dereference.
// Note that this is the only place where the `FlowState` is used in this configuration.
node = InvalidPointerToDerefBarrier::getABarrierNode(pai)
}
}
private import DataFlow::Global<InvalidPointerToDerefConfig>
private import DataFlow::GlobalWithState<InvalidPointerToDerefConfig>
/**
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
@@ -165,19 +194,14 @@ private predicate invalidPointerToDerefSource(
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
int deltaDerefSourceAndPai
) {
exists(int rhsSizeDelta |
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
// and the instruction computing the address for which we will search for a dereference.
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, rhsSizeDelta) and
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
deltaDerefSourceAndPai >= 0 and
// TODO: This condition will go away once #13725 is merged, and then we can make `SizeBarrier`
// private to `AllocationToInvalidPointer.qll`.
not derefSource.getBasicBlock() =
AllocToInvalidPointer::SizeBarrier::getABarrierBlock(rhsSizeDelta)
)
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
// and the instruction computing the address for which we will search for a dereference.
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, _) and
// derefSource <= pai + deltaDerefSourceAndPai
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
deltaDerefSourceAndPai >= 0
}
/**
@@ -187,15 +211,14 @@ private predicate invalidPointerToDerefSource(
*/
pragma[inline]
private predicate isInvalidPointerDerefSink(
DataFlow::Node sink, Instruction i, string operation, int deltaDerefSinkAndDerefAddress
DataFlow::Node sink, AddressOperand addr, Instruction i, string operation,
int deltaDerefSinkAndDerefAddress
) {
exists(AddressOperand addr, Instruction s, IRBlock b |
exists(Instruction s |
s = sink.asInstruction() and
bounded(addr.getDef(), s, deltaDerefSinkAndDerefAddress) and
deltaDerefSinkAndDerefAddress >= 0 and
i.getAnOperand() = addr and
b = i.getBlock() and
not b = InvalidPointerToDerefBarrier::getABarrierBlock(deltaDerefSinkAndDerefAddress)
i.getAnOperand() = addr
|
i instanceof StoreInstruction and
operation = "write"
@@ -221,9 +244,11 @@ private Instruction getASuccessor(Instruction instr) {
instr.getBlock().getASuccessor+() = result.getBlock()
}
private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFlow::Node derefSink) {
private predicate paiForDereferenceSink(
PointerArithmeticInstruction pai, DataFlow::Node derefSink, int deltaDerefSourceAndPai
) {
exists(DataFlow::Node derefSource |
invalidPointerToDerefSource(_, pai, derefSource, _) and
invalidPointerToDerefSource(_, pai, derefSource, deltaDerefSourceAndPai) and
flow(derefSource, derefSink)
)
}
@@ -235,13 +260,15 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
*/
private predicate derefSinkToOperation(
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
string description, int deltaDerefSinkAndDerefAddress
string description, int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress
) {
exists(Instruction operationInstr |
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
isInvalidPointerDerefSink(derefSink, operationInstr, description, deltaDerefSinkAndDerefAddress) and
exists(Instruction operationInstr, AddressOperand addr |
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink), deltaDerefSourceAndPai) and
isInvalidPointerDerefSink(derefSink, addr, operationInstr, description,
deltaDerefSinkAndDerefAddress) and
operationInstr = getASuccessor(derefSink.asInstruction()) and
operation.asInstruction() = operationInstr
operation.asInstruction() = operationInstr and
not addr = InvalidPointerToDerefBarrier::getABarrierAddressOperand(pai)
)
}
@@ -260,7 +287,8 @@ predicate operationIsOffBy(
exists(int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress |
invalidPointerToDerefSource(allocation, pai, derefSource, deltaDerefSourceAndPai) and
flow(derefSource, derefSink) and
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSinkAndDerefAddress) and
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSourceAndPai,
deltaDerefSinkAndDerefAddress) and
delta = deltaDerefSourceAndPai + deltaDerefSinkAndDerefAddress
)
}

View File

@@ -18,7 +18,7 @@ private Instruction getABoundIn(SemBound b, IRFunction func) {
* Holds if `i <= b + delta`.
*/
pragma[inline]
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
private predicate boundedImplCand(Instruction i, Instruction b, int delta) {
exists(SemBound bound, IRFunction func |
semBounded(getSemanticExpr(i), bound, delta, true, _) and
b = getABoundIn(bound, func) and
@@ -26,6 +26,15 @@ private predicate boundedImpl(Instruction i, Instruction b, int delta) {
)
}
/**
* Holds if `i <= b + delta` and `delta` is the smallest integer that satisfies
* this condition.
*/
pragma[inline]
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
delta = min(int cand | boundedImplCand(i, b, cand))
}
/**
* Holds if `i <= b + delta`.
*

View File

@@ -69,12 +69,6 @@ edges
| test.cpp:322:19:322:27 | ... + ... | test.cpp:325:24:325:26 | end |
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... |
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 |
| test.cpp:351:9:351:11 | arr | test.cpp:351:9:351:14 | access to array |
| test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array |
| test.cpp:351:18:351:20 | arr | test.cpp:351:9:351:14 | access to array |
| test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array |
| test.cpp:351:29:351:31 | arr | test.cpp:351:9:351:14 | access to array |
| test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array |
nodes
| test.cpp:34:5:34:24 | access to array | semmle.label | access to array |
| test.cpp:34:10:34:12 | buf | semmle.label | buf |
@@ -167,11 +161,6 @@ nodes
| test.cpp:325:15:325:19 | temp2 | semmle.label | temp2 |
| test.cpp:325:24:325:26 | end | semmle.label | end |
| test.cpp:325:24:325:26 | end | semmle.label | end |
| test.cpp:351:9:351:11 | arr | semmle.label | arr |
| test.cpp:351:9:351:14 | access to array | semmle.label | access to array |
| test.cpp:351:18:351:20 | arr | semmle.label | arr |
| test.cpp:351:18:351:25 | access to array | semmle.label | access to array |
| test.cpp:351:29:351:31 | arr | semmle.label | arr |
subpaths
#select
| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write |
@@ -194,6 +183,3 @@ subpaths
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:330:13:330:24 | Store: ... = ... | write |
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:331:13:331:24 | Store: ... = ... | write |
| test.cpp:322:19:322:27 | PointerAdd: ... + ... | test.cpp:322:19:322:22 | temp | test.cpp:325:24:325:26 | end | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:314:10:314:13 | temp | temp | test.cpp:333:13:333:24 | Store: ... = ... | write |
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:9:351:11 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:18:351:20 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |
| test.cpp:351:18:351:25 | PointerAdd: access to array | test.cpp:351:29:351:31 | arr | test.cpp:351:18:351:25 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:348:9:348:11 | arr | arr | test.cpp:351:18:351:25 | Load: access to array | read |

View File

@@ -348,7 +348,7 @@ int positiveRange(int x) {
int arr[128];
for(int i=127-offset; i>= 0; i--) {
arr[i] = arr[i+1] + arr[i+offset]; // GOOD [FALSE POSITIVE]
arr[i] = arr[i+1] + arr[i+offset]; // GOOD
}
return arr[0];
}

View File

@@ -129,7 +129,6 @@ edges
| test.cpp:271:14:271:21 | ... + ... | test.cpp:271:14:271:21 | ... + ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | ... = ... |
| test.cpp:271:14:271:21 | ... + ... | test.cpp:274:5:274:10 | ... = ... |
| test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | ... = ... |
| test.cpp:355:14:355:27 | new[] | test.cpp:356:15:356:23 | ... + ... |
| test.cpp:355:14:355:27 | new[] | test.cpp:356:15:356:23 | ... + ... |
| test.cpp:355:14:355:27 | new[] | test.cpp:357:24:357:30 | ... + ... |
@@ -214,20 +213,21 @@ edges
| test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | ... = ... |
| test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | ... = ... |
| test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | ... = ... |
| test.cpp:652:14:652:27 | new[] | test.cpp:656:3:656:6 | ... ++ |
| test.cpp:652:14:652:27 | new[] | test.cpp:656:3:656:6 | ... ++ |
| test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | ... = ... |
| test.cpp:656:3:656:6 | ... ++ | test.cpp:656:3:656:6 | ... ++ |
| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... |
| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | ... = ... |
| test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... |
| test.cpp:695:13:695:26 | new[] | test.cpp:698:5:698:10 | ... += ... |
| test.cpp:695:13:695:26 | new[] | test.cpp:698:5:698:10 | ... += ... |
| test.cpp:698:5:698:10 | ... += ... | test.cpp:698:5:698:10 | ... += ... |
| test.cpp:698:5:698:10 | ... += ... | test.cpp:701:15:701:16 | * ... |
| test.cpp:705:18:705:18 | q | test.cpp:705:18:705:18 | q |
| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... |
| test.cpp:705:18:705:18 | q | test.cpp:706:12:706:13 | * ... |
| test.cpp:711:13:711:26 | new[] | test.cpp:714:11:714:11 | q |
| test.cpp:714:11:714:11 | q | test.cpp:705:18:705:18 | q |
| test.cpp:730:12:730:28 | new[] | test.cpp:732:16:732:26 | ... + ... |
| test.cpp:730:12:730:28 | new[] | test.cpp:732:16:732:26 | ... + ... |
| test.cpp:730:12:730:28 | new[] | test.cpp:733:5:733:12 | ... = ... |
| test.cpp:732:16:732:26 | ... + ... | test.cpp:732:16:732:26 | ... + ... |
| test.cpp:732:16:732:26 | ... + ... | test.cpp:733:5:733:12 | ... = ... |
| test.cpp:732:16:732:26 | ... + ... | test.cpp:733:5:733:12 | ... = ... |
nodes
| test.cpp:4:15:4:20 | call to malloc | semmle.label | call to malloc |
| test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... |
@@ -320,8 +320,6 @@ nodes
| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
| test.cpp:274:5:274:10 | ... = ... | semmle.label | ... = ... |
| test.cpp:304:15:304:26 | new[] | semmle.label | new[] |
| test.cpp:308:5:308:29 | ... = ... | semmle.label | ... = ... |
| test.cpp:355:14:355:27 | new[] | semmle.label | new[] |
| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
@@ -371,20 +369,19 @@ nodes
| test.cpp:559:5:559:19 | ... = ... | semmle.label | ... = ... |
| test.cpp:642:14:642:31 | new[] | semmle.label | new[] |
| test.cpp:647:5:647:19 | ... = ... | semmle.label | ... = ... |
| test.cpp:652:14:652:27 | new[] | semmle.label | new[] |
| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ |
| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ |
| test.cpp:662:3:662:11 | ... = ... | semmle.label | ... = ... |
| test.cpp:667:14:667:31 | new[] | semmle.label | new[] |
| test.cpp:675:7:675:23 | ... = ... | semmle.label | ... = ... |
| test.cpp:695:13:695:26 | new[] | semmle.label | new[] |
| test.cpp:698:5:698:10 | ... += ... | semmle.label | ... += ... |
| test.cpp:698:5:698:10 | ... += ... | semmle.label | ... += ... |
| test.cpp:701:15:701:16 | * ... | semmle.label | * ... |
| test.cpp:705:18:705:18 | q | semmle.label | q |
| test.cpp:705:18:705:18 | q | semmle.label | q |
| test.cpp:706:12:706:13 | * ... | semmle.label | * ... |
| test.cpp:711:13:711:26 | new[] | semmle.label | new[] |
| test.cpp:714:11:714:11 | q | semmle.label | q |
| test.cpp:730:12:730:28 | new[] | semmle.label | new[] |
| test.cpp:732:16:732:26 | ... + ... | semmle.label | ... + ... |
| test.cpp:732:16:732:26 | ... + ... | semmle.label | ... + ... |
| test.cpp:733:5:733:12 | ... = ... | semmle.label | ... = ... |
subpaths
#select
| test.cpp:6:14:6:15 | * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
@@ -406,7 +403,6 @@ subpaths
| test.cpp:254:9:254:16 | ... = ... | test.cpp:248:24:248:30 | call to realloc | test.cpp:254:9:254:16 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:248:24:248:30 | call to realloc | call to realloc | test.cpp:254:11:254:11 | i | i |
| test.cpp:264:13:264:14 | * ... | test.cpp:260:13:260:24 | new[] | test.cpp:264:13:264:14 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:260:13:260:24 | new[] | new[] | test.cpp:261:19:261:21 | len | len |
| test.cpp:274:5:274:10 | ... = ... | test.cpp:270:13:270:24 | new[] | test.cpp:274:5:274:10 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:270:13:270:24 | new[] | new[] | test.cpp:271:19:271:21 | len | len |
| test.cpp:308:5:308:29 | ... = ... | test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:304:15:304:26 | new[] | new[] | test.cpp:308:8:308:10 | ... + ... | ... + ... |
| test.cpp:358:14:358:26 | * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
| test.cpp:359:14:359:32 | * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 2. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
| test.cpp:384:13:384:16 | * ... | test.cpp:377:14:377:27 | new[] | test.cpp:384:13:384:16 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:377:14:377:27 | new[] | new[] | test.cpp:378:20:378:23 | size | size |
@@ -418,7 +414,6 @@ subpaths
| test.cpp:548:5:548:19 | ... = ... | test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:543:14:543:27 | new[] | new[] | test.cpp:548:8:548:14 | src_pos | src_pos |
| test.cpp:559:5:559:19 | ... = ... | test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:554:14:554:27 | new[] | new[] | test.cpp:559:8:559:14 | src_pos | src_pos |
| test.cpp:647:5:647:19 | ... = ... | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:642:14:642:31 | new[] | new[] | test.cpp:647:8:647:14 | src_pos | src_pos |
| test.cpp:662:3:662:11 | ... = ... | test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:652:14:652:27 | new[] | new[] | test.cpp:653:19:653:22 | size | size |
| test.cpp:675:7:675:23 | ... = ... | test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:667:14:667:31 | new[] | new[] | test.cpp:675:10:675:18 | ... ++ | ... ++ |
| test.cpp:701:15:701:16 | * ... | test.cpp:695:13:695:26 | new[] | test.cpp:701:15:701:16 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:695:13:695:26 | new[] | new[] | test.cpp:696:19:696:22 | size | size |
| test.cpp:706:12:706:13 | * ... | test.cpp:711:13:711:26 | new[] | test.cpp:706:12:706:13 | * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:711:13:711:26 | new[] | new[] | test.cpp:712:19:712:22 | size | size |
| test.cpp:733:5:733:12 | ... = ... | test.cpp:730:12:730:28 | new[] | test.cpp:733:5:733:12 | ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:730:12:730:28 | new[] | new[] | test.cpp:732:21:732:25 | ... + ... | ... + ... |

View File

@@ -305,7 +305,7 @@ void test21() {
for (int i = 0; i < n; i += 2) {
xs[i] = test21_get(i); // GOOD
xs[i+1] = test21_get(i+1); // $ alloc=L304 alloc=L304-1 deref=L308 // GOOD [FALSE POSITIVE]
xs[i+1] = test21_get(i+1); // GOOD
}
}
@@ -659,7 +659,7 @@ void test32(unsigned size) {
xs++;
if (xs >= end)
return;
xs[0] = 0; // $ deref=L656->L662+1 deref=L657->L662+1 GOOD [FALSE POSITIVE]
xs[0] = 0; // GOOD
}
void test33(unsigned size, unsigned src_pos)
@@ -672,7 +672,7 @@ void test33(unsigned size, unsigned src_pos)
while (dst_pos < size - 1) {
dst_pos++;
if (true)
xs[dst_pos++] = 0; // $ alloc=L667+1 deref=L675 // GOOD [FALSE POSITIVE]
xs[dst_pos++] = 0; // GOOD
}
}
@@ -714,3 +714,31 @@ void test35(unsigned long size, char* q)
deref(q);
}
}
void test21_simple(bool b) {
int n = 0;
if (b) n = 2;
int* xs = new int[n];
for (int i = 0; i < n; i += 2) {
xs[i+1] = 0; // GOOD
}
}
void test36(unsigned size, unsigned n) {
int* p = new int[size + 2];
if(n < size + 1) {
int* end = p + (n + 2); // $ alloc=L730+2
*end = 0; // $ deref=L733 // BAD
}
}
void test37(unsigned long n)
{
int *p = new int[n];
for (unsigned long i = n; i != 0u; i--)
{
p[n - i] = 0; // GOOD
}
}

View File

@@ -732,7 +732,7 @@ void test_does_not_write_source_to_dereference()
{
int x;
does_not_write_source_to_dereference(&x);
sink(x); // $ ast,ir=733:7 SPURIOUS: ast,ir=726:11
sink(x); // $ ast=733:7 ir SPURIOUS: ast=726:11
}
void sometimes_calls_sink_eq(int x, int n) {

View File

@@ -134,7 +134,7 @@ void pointer_test() {
sink(*p3); // $ ast,ir
*p3 = 0;
sink(*p3); // $ SPURIOUS: ast,ir
sink(*p3); // $ SPURIOUS: ast
}
// --- return values ---

View File

@@ -1,3 +0,0 @@
| file://:0:0:0:0 | There was an error during this compilation |
| float128.cpp:1:39:1:39 | 128-bit floating-point types are not supported in this configuration |
| float128.cpp:2:30:2:30 | 128-bit floating-point types are not supported in this configuration |

View File

@@ -1,4 +0,0 @@
import cpp
from Diagnostic d
select d

View File

@@ -1,5 +1,5 @@
typedef _Complex float __attribute__((mode(TC))) _Complex128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED]
typedef float __attribute__((mode(TF))) _Float128; // [COMPILER ERROR AND ERROR-TYPE DUE TO __float128 BEING DISABLED]
typedef _Complex float __attribute__((mode(TC))) _Complex128;
typedef float __attribute__((mode(TF))) _Float128;
int main() {
__float128 f = 1.0f;
@@ -25,4 +25,3 @@ __float128 id(__float128 q)
{
return q;
}
// semmle-extractor-options: --expect_errors

View File

@@ -1,5 +1,5 @@
| float128.cpp:1:50:1:60 | _Complex128 | file://:0:0:0:0 | <error-type> |
| float128.cpp:2:41:2:49 | _Float128 | file://:0:0:0:0 | <error-type> |
| float128.cpp:1:50:1:60 | _Complex128 | file://:0:0:0:0 | float __complex__ |
| float128.cpp:2:41:2:49 | _Float128 | file://:0:0:0:0 | __float128 |
| float128.cpp:13:29:13:54 | __is_floating_point_helper<T> | float128.cpp:10:8:10:17 | false_type |
| float128.cpp:14:19:14:51 | __is_floating_point_helper<float> | float128.cpp:11:8:11:16 | true_type |
| float128.cpp:15:19:15:52 | __is_floating_point_helper<double> | float128.cpp:11:8:11:16 | true_type |

View File

@@ -95,3 +95,25 @@ void gotoLoop(bool b1, bool b2)
}
}
}
void test_sub(int x, int y, int n) {
if(x > 0 && x < 500) {
if(y > 0 && y < 10) {
range(x - y); // $ range=<=498 range=>=-8
}
if(n > 0 && n < 100) {
for (int i = 0; i < n; i++)
{
range(n - i); // $ range=">=Phi: i-97" range=<=99 range=>=-97
range(i - n); // $ range="<=Phi: i-1" range=">=Phi: i-99" range=<=97 range=>=-99
}
for (int i = n; i != 0; i--)
{
range(n - i); // $ SPURIOUS: overflow=+
range(i - n); // $ range=">=Phi: i-99"
}
}
}
}

View File

@@ -725,7 +725,7 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestWindowsCmdIgnoreErrors()
{
actions.RunProcess["cmd.exe /C ^\"build.cmd --skip-tests^\""] = 3;
actions.RunProcess["cmd.exe /C ^\"build.cmd^ --skip-tests^\""] = 3;
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\codeql index --xml --extensions config"] = 0;
actions.FileExists["csharp.log"] = true;
@@ -744,9 +744,9 @@ namespace Semmle.Autobuild.CSharp.Tests
public void TestWindowCSharpMsBuild()
{
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test2.sln -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -775,9 +775,9 @@ namespace Semmle.Autobuild.CSharp.Tests
public void TestWindowCSharpMsBuildMultipleSolutions()
{
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.csproj -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test2.csproj -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.csproj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project\test1.csproj"] = true;
actions.FileExists[@"C:\Project\test2.csproj"] = true;
@@ -820,7 +820,7 @@ namespace Semmle.Autobuild.CSharp.Tests
public void TestWindowCSharpMsBuildFailed()
{
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 1;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 1;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -846,8 +846,8 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestSkipNugetMsBuild()
{
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -1037,7 +1037,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\dirs.proj -DisableParallelProcessing"] = 1;
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\dirs.proj -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\dirs.proj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program^ Files^ ^(x86^)\\Microsoft^ Visual^ Studio^ 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\dirs.proj /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project\a\test.csproj"] = true;
actions.FileExists[@"C:\Project\dirs.proj"] = true;

View File

@@ -55,7 +55,7 @@ namespace Semmle.Autobuild.Shared
}
private static readonly char[] specialChars = { ' ', '\t', '\n', '\v', '\"' };
private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|' };
private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|', ' ' };
/// <summary>
/// Appends the given argument to the command line.

View File

@@ -8,14 +8,13 @@ using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
namespace Semmle.BuildAnalyser
{
/// <summary>
/// Main implementation of the build analysis.
/// </summary>
internal sealed partial class BuildAnalysis : IDisposable
internal sealed class BuildAnalysis : IDisposable
{
private readonly AssemblyCache assemblyCache;
private readonly ProgressMonitor progressMonitor;
@@ -29,6 +28,9 @@ namespace Semmle.BuildAnalyser
private readonly Options options;
private readonly DirectoryInfo sourceDir;
private readonly DotNet dotnet;
private readonly FileContent fileContent;
private readonly TemporaryDirectory packageDirectory;
/// <summary>
/// Performs a C# build analysis.
@@ -55,6 +57,9 @@ namespace Semmle.BuildAnalyser
this.progressMonitor.FindingFiles(options.SrcDir);
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
this.fileContent = new FileContent(packageDirectory, progressMonitor, () => GetFiles("*.*"));
this.allSources = GetFiles("*.cs").ToArray();
var allProjects = GetFiles("*.csproj");
var solutions = options.SolutionFile is not null
@@ -63,12 +68,19 @@ namespace Semmle.BuildAnalyser
var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList();
// Find DLLs in the .Net Framework
// Find DLLs in the .Net / Asp.Net Framework
if (options.ScanNetFrameworkDlls)
{
var runtimeLocation = new Runtime(dotnet).GetRuntime(options.UseSelfContainedDotnet);
progressMonitor.Log(Util.Logging.Severity.Debug, $"Runtime location selected: {runtimeLocation}");
var runtime = new Runtime(dotnet);
var runtimeLocation = runtime.GetRuntime(options.UseSelfContainedDotnet);
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
dllDirNames.Add(runtimeLocation);
if (fileContent.UseAspNetDlls && runtime.GetAspRuntime() is string aspRuntime)
{
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspRuntime}");
dllDirNames.Add(aspRuntime);
}
}
if (options.UseMscorlib)
@@ -76,8 +88,6 @@ namespace Semmle.BuildAnalyser
UseReference(typeof(object).Assembly.Location);
}
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
if (options.UseNuGet)
{
dllDirNames.Add(packageDirectory.DirInfo.FullName);
@@ -187,6 +197,7 @@ namespace Semmle.BuildAnalyser
{
finalAssemblyList[r.Name] = r;
}
// Update the used references list
usedReferences.Clear();
foreach (var r in finalAssemblyList.Select(r => r.Value.Filename))
@@ -210,24 +221,18 @@ namespace Semmle.BuildAnalyser
/// Store that a particular reference file is used.
/// </summary>
/// <param name="reference">The filename of the reference.</param>
private void UseReference(string reference)
{
usedReferences[reference] = true;
}
private void UseReference(string reference) => usedReferences[reference] = true;
/// <summary>
/// Store that a particular source file is used (by a project file).
/// </summary>
/// <param name="sourceFile">The source file.</param>
private void UseSource(FileInfo sourceFile)
{
sources[sourceFile.FullName] = sourceFile.Exists;
}
private void UseSource(FileInfo sourceFile) => sources[sourceFile.FullName] = sourceFile.Exists;
/// <summary>
/// The list of resolved reference files.
/// </summary>
public IEnumerable<string> ReferenceFiles => this.usedReferences.Keys;
public IEnumerable<string> ReferenceFiles => usedReferences.Keys;
/// <summary>
/// The list of source files used in projects.
@@ -242,7 +247,7 @@ namespace Semmle.BuildAnalyser
/// <summary>
/// List of assembly IDs which couldn't be resolved.
/// </summary>
public IEnumerable<string> UnresolvedReferences => this.unresolvedReferences.Select(r => r.Key);
public IEnumerable<string> UnresolvedReferences => unresolvedReferences.Select(r => r.Key);
/// <summary>
/// List of source files which were mentioned in project files but
@@ -256,12 +261,7 @@ namespace Semmle.BuildAnalyser
/// </summary>
/// <param name="id">The assembly ID.</param>
/// <param name="projectFile">The project file making the reference.</param>
private void UnresolvedReference(string id, string projectFile)
{
unresolvedReferences[id] = projectFile;
}
private readonly TemporaryDirectory packageDirectory;
private void UnresolvedReference(string id, string projectFile) => unresolvedReferences[id] = projectFile;
/// <summary>
/// Reads all the source files and references from the given list of projects.
@@ -318,10 +318,8 @@ namespace Semmle.BuildAnalyser
}
private bool Restore(string target, string? pathToNugetConfig = null)
{
return dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
}
private bool Restore(string target, string? pathToNugetConfig = null) =>
dotnet.RestoreToDirectory(target, packageDirectory.DirInfo.FullName, pathToNugetConfig);
private void Restore(IEnumerable<string> targets, string? pathToNugetConfig = null)
{
@@ -331,11 +329,9 @@ namespace Semmle.BuildAnalyser
}
}
private void DownloadMissingPackages(IEnumerable<string> restoreTargets)
{
var alreadyDownloadedPackages = Directory.GetDirectories(packageDirectory.DirInfo.FullName).Select(d => Path.GetFileName(d).ToLowerInvariant()).ToHashSet();
var notYetDownloadedPackages = new HashSet<string>();
var nugetConfigs = GetFiles("nuget.config", recurseSubdirectories: true).ToArray();
string? nugetConfig = null;
if (nugetConfigs.Length > 1)
@@ -352,46 +348,7 @@ namespace Semmle.BuildAnalyser
nugetConfig = nugetConfigs.FirstOrDefault();
}
var allFiles = GetFiles("*.*");
foreach (var file in allFiles)
{
try
{
using var sr = new StreamReader(file);
ReadOnlySpan<char> line;
while ((line = sr.ReadLine()) != null)
{
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
{
// We can't get the group from the ValueMatch, so doing it manually:
var match = line.Slice(valueMatch.Index, valueMatch.Length);
var includeIndex = match.IndexOf("Include", StringComparison.InvariantCultureIgnoreCase);
if (includeIndex == -1)
{
continue;
}
match = match.Slice(includeIndex + "Include".Length + 1);
var quoteIndex1 = match.IndexOf("\"");
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
var packageName = match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant();
if (!alreadyDownloadedPackages.Contains(packageName))
{
notYetDownloadedPackages.Add(packageName);
}
}
}
}
catch (Exception ex)
{
progressMonitor.FailedToReadFile(file, ex);
continue;
}
}
foreach (var package in notYetDownloadedPackages)
foreach (var package in fileContent.NotYetDownloadedPackages)
{
progressMonitor.NugetInstall(package);
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package));
@@ -434,12 +391,6 @@ namespace Semmle.BuildAnalyser
});
}
public void Dispose()
{
packageDirectory?.Dispose();
}
[GeneratedRegex("<PackageReference .*Include=\"(.*?)\".*/>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex PackageReference();
public void Dispose() => packageDirectory?.Dispose();
}
}

View File

@@ -10,7 +10,7 @@ namespace Semmle.BuildAnalyser
bool RestoreToDirectory(string project, string directory, string? pathToNugetConfig = null);
bool New(string folder);
bool AddPackage(string folder, string package);
public IList<string> GetListedRuntimes();
IList<string> GetListedRuntimes();
}
/// <summary>
@@ -78,7 +78,8 @@ namespace Semmle.BuildAnalyser
public IList<string> GetListedRuntimes()
{
var args = "--list-runtimes";
const string args = "--list-runtimes";
progressMonitor.RunningProcess($"{dotnet} {args}");
var pi = new ProcessStartInfo(dotnet, args)
{
RedirectStandardOutput = true,
@@ -90,6 +91,7 @@ namespace Semmle.BuildAnalyser
progressMonitor.CommandFailed(dotnet, args, exitCode);
return new List<string>();
}
progressMonitor.LogInfo($"Found runtimes: {string.Join("\n", runtimes)}");
return runtimes;
}
}

View File

@@ -0,0 +1,166 @@
using Semmle.Util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Semmle.BuildAnalyser
{
// <summary>
// This class is used to read a set of files and decide different properties about the
// content (by reading the content of the files only once).
// The implementation is lazy, so the properties are only calculated when
// the first property is accessed.
// </summary>
internal partial class FileContent
{
private readonly ProgressMonitor progressMonitor;
private readonly IUnsafeFileReader unsafeFileReader;
private readonly Func<IEnumerable<string>> getFiles;
private readonly Func<HashSet<string>> getAlreadyDownloadedPackages;
private readonly HashSet<string> notYetDownloadedPackages = new HashSet<string>();
private readonly Initializer initialize;
public HashSet<string> NotYetDownloadedPackages
{
get
{
initialize.Run();
return notYetDownloadedPackages;
}
}
private bool useAspNetDlls = false;
/// <summary>
/// True if any file in the source directory indicates that ASP.NET is used.
/// The following heuristic is used to decide, if ASP.NET is used:
/// If any file in the source directory contains something like (this will most like be a .csproj file)
/// <Project Sdk="Microsoft.NET.Sdk.Web">
/// <FrameworkReference Include="Microsoft.AspNetCore.App"/>
/// </summary>
public bool UseAspNetDlls
{
get
{
initialize.Run();
return useAspNetDlls;
}
}
internal FileContent(Func<HashSet<string>> getAlreadyDownloadedPackages,
ProgressMonitor progressMonitor,
Func<IEnumerable<string>> getFiles,
IUnsafeFileReader unsafeFileReader)
{
this.getAlreadyDownloadedPackages = getAlreadyDownloadedPackages;
this.progressMonitor = progressMonitor;
this.getFiles = getFiles;
this.unsafeFileReader = unsafeFileReader;
this.initialize = new Initializer(DoInitialize);
}
public FileContent(TemporaryDirectory packageDirectory, ProgressMonitor progressMonitor, Func<IEnumerable<string>> getFiles) : this(() => Directory.GetDirectories(packageDirectory.DirInfo.FullName)
.Select(d => Path.GetFileName(d)
.ToLowerInvariant())
.ToHashSet(), progressMonitor, getFiles, new UnsafeFileReader())
{ }
private static string GetGroup(ReadOnlySpan<char> input, ValueMatch valueMatch, string groupPrefix)
{
var match = input.Slice(valueMatch.Index, valueMatch.Length);
var includeIndex = match.IndexOf(groupPrefix, StringComparison.InvariantCultureIgnoreCase);
if (includeIndex == -1)
{
return string.Empty;
}
match = match.Slice(includeIndex + groupPrefix.Length + 1);
var quoteIndex1 = match.IndexOf("\"");
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
return match.Slice(quoteIndex1 + 1, quoteIndex2).ToString().ToLowerInvariant();
}
private static bool IsGroupMatch(ReadOnlySpan<char> line, Regex regex, string groupPrefix, string value)
{
foreach (var valueMatch in regex.EnumerateMatches(line))
{
// We can't get the group from the ValueMatch, so doing it manually:
if (GetGroup(line, valueMatch, groupPrefix) == value.ToLowerInvariant())
{
return true;
}
}
return false;
}
private void DoInitialize()
{
var alreadyDownloadedPackages = getAlreadyDownloadedPackages();
foreach (var file in getFiles())
{
try
{
foreach (ReadOnlySpan<char> line in unsafeFileReader.ReadLines(file))
{
// Find the not yet downloaded packages.
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
{
// We can't get the group from the ValueMatch, so doing it manually:
var packageName = GetGroup(line, valueMatch, "Include");
if (!string.IsNullOrEmpty(packageName) && !alreadyDownloadedPackages.Contains(packageName))
{
notYetDownloadedPackages.Add(packageName);
}
}
// Determine if ASP.NET is used.
if (!useAspNetDlls)
{
useAspNetDlls =
IsGroupMatch(line, ProjectSdk(), "Sdk", "Microsoft.NET.Sdk.Web") ||
IsGroupMatch(line, FrameworkReference(), "Include", "Microsoft.AspNetCore.App");
}
}
}
catch (Exception ex)
{
progressMonitor.FailedToReadFile(file, ex);
}
}
}
[GeneratedRegex("<PackageReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex PackageReference();
[GeneratedRegex("<FrameworkReference.*\\sInclude=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex FrameworkReference();
[GeneratedRegex("<(.*\\s)?Project.*\\sSdk=\"(.*?)\".*/?>", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline)]
private static partial Regex ProjectSdk();
}
}
internal interface IUnsafeFileReader
{
IEnumerable<string> ReadLines(string file);
}
internal class UnsafeFileReader : IUnsafeFileReader
{
public IEnumerable<string> ReadLines(string file)
{
using var sr = new StreamReader(file);
string? line;
while ((line = sr.ReadLine()) != null)
{
yield return line;
}
}
}

View File

@@ -12,121 +12,101 @@ namespace Semmle.BuildAnalyser
this.logger = logger;
}
public void FindingFiles(string dir)
{
logger.Log(Severity.Info, "Finding files in {0}...", dir);
}
public void Log(Severity severity, string message) =>
logger.Log(severity, message);
public void LogInfo(string message) =>
logger.Log(Severity.Info, message);
private void LogDebug(string message) =>
logger.Log(Severity.Debug, message);
private void LogError(string message) =>
logger.Log(Severity.Error, message);
public void FindingFiles(string dir) =>
LogInfo($"Finding files in {dir}...");
public void IndexingReferences(int count)
{
logger.Log(Severity.Info, "Indexing...");
logger.Log(Severity.Debug, "Indexing {0} DLLs...", count);
LogInfo("Indexing...");
LogDebug($"Indexing {count} DLLs...");
}
public void UnresolvedReference(string id, string project)
{
logger.Log(Severity.Info, "Unresolved reference {0}", id);
logger.Log(Severity.Debug, "Unresolved {0} referenced by {1}", id, project);
LogInfo($"Unresolved reference {id}");
LogDebug($"Unresolved {id} referenced by {project}");
}
public void AnalysingSolution(string filename)
{
logger.Log(Severity.Info, $"Analyzing {filename}...");
}
public void AnalysingSolution(string filename) =>
LogInfo($"Analyzing {filename}...");
public void FailedProjectFile(string filename, string reason)
{
logger.Log(Severity.Info, "Couldn't read project file {0}: {1}", filename, reason);
}
public void FailedProjectFile(string filename, string reason) =>
LogInfo($"Couldn't read project file {filename}: {reason}");
public void FailedNugetCommand(string exe, string args, string message)
{
logger.Log(Severity.Info, "Command failed: {0} {1}", exe, args);
logger.Log(Severity.Info, " {0}", message);
LogInfo($"Command failed: {exe} {args}");
LogInfo($" {message}");
}
public void NugetInstall(string package)
{
logger.Log(Severity.Info, "Restoring {0}...", package);
}
public void NugetInstall(string package) =>
LogInfo($"Restoring {package}...");
public void ResolvedReference(string filename)
{
logger.Log(Severity.Info, "Resolved {0}", filename);
}
public void ResolvedReference(string filename) =>
LogInfo($"Resolved {filename}");
public void Summary(int existingSources, int usedSources, int missingSources,
int references, int unresolvedReferences,
int resolvedConflicts, int totalProjects, int failedProjects,
TimeSpan analysisTime)
{
logger.Log(Severity.Info, "");
logger.Log(Severity.Info, "Build analysis summary:");
logger.Log(Severity.Info, "{0, 6} source files in the filesystem", existingSources);
logger.Log(Severity.Info, "{0, 6} source files in project files", usedSources);
logger.Log(Severity.Info, "{0, 6} sources missing from project files", missingSources);
logger.Log(Severity.Info, "{0, 6} resolved references", references);
logger.Log(Severity.Info, "{0, 6} unresolved references", unresolvedReferences);
logger.Log(Severity.Info, "{0, 6} resolved assembly conflicts", resolvedConflicts);
logger.Log(Severity.Info, "{0, 6} projects", totalProjects);
logger.Log(Severity.Info, "{0, 6} missing/failed projects", failedProjects);
logger.Log(Severity.Info, "Build analysis completed in {0}", analysisTime);
const int align = 6;
LogInfo("");
LogInfo("Build analysis summary:");
LogInfo($"{existingSources,align} source files in the filesystem");
LogInfo($"{usedSources,align} source files in project files");
LogInfo($"{missingSources,align} sources missing from project files");
LogInfo($"{references,align} resolved references");
LogInfo($"{unresolvedReferences,align} unresolved references");
LogInfo($"{resolvedConflicts,align} resolved assembly conflicts");
LogInfo($"{totalProjects,align} projects");
LogInfo($"{failedProjects,align} missing/failed projects");
LogInfo($"Build analysis completed in {analysisTime}");
}
public void Log(Severity severity, string message)
{
logger.Log(severity, message);
}
public void ResolvedConflict(string asm1, string asm2) =>
LogDebug($"Resolved {asm1} as {asm2}");
public void ResolvedConflict(string asm1, string asm2)
{
logger.Log(Severity.Debug, "Resolved {0} as {1}", asm1, asm2);
}
public void MissingProject(string projectFile) =>
LogInfo($"Solution is missing {projectFile}");
public void MissingProject(string projectFile)
{
logger.Log(Severity.Info, "Solution is missing {0}", projectFile);
}
public void CommandFailed(string exe, string arguments, int exitCode) =>
LogError($"Command {exe} {arguments} failed with exit code {exitCode}");
public void CommandFailed(string exe, string arguments, int exitCode)
{
logger.Log(Severity.Error, $"Command {exe} {arguments} failed with exit code {exitCode}");
}
public void MissingNuGet() =>
LogError("Missing nuget.exe");
public void MissingNuGet()
{
logger.Log(Severity.Error, "Missing nuget.exe");
}
public void MissingDotNet() =>
LogError("Missing dotnet CLI");
public void MissingDotNet()
{
logger.Log(Severity.Error, "Missing dotnet CLI");
}
public void RunningProcess(string command) =>
LogInfo($"Running {command}");
public void RunningProcess(string command)
{
logger.Log(Severity.Info, $"Running {command}");
}
public void FailedToRestoreNugetPackage(string package)
{
logger.Log(Severity.Info, $"Failed to restore nuget package {package}");
}
public void FailedToRestoreNugetPackage(string package) =>
LogInfo($"Failed to restore nuget package {package}");
public void FailedToReadFile(string file, Exception ex)
{
logger.Log(Severity.Info, $"Failed to read file {file}");
logger.Log(Severity.Debug, $"Failed to read file {file}, exception: {ex}");
LogInfo($"Failed to read file {file}");
LogDebug($"Failed to read file {file}, exception: {ex}");
}
public void MultipleNugetConfig(string[] nugetConfigs)
{
logger.Log(Severity.Info, $"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}.");
}
public void MultipleNugetConfig(string[] nugetConfigs) =>
LogInfo($"Found multiple nuget.config files: {string.Join(", ", nugetConfigs)}.");
internal void NoTopLevelNugetConfig()
{
logger.Log(Severity.Info, $"Could not find a top-level nuget.config file.");
}
internal void NoTopLevelNugetConfig() =>
LogInfo("Could not find a top-level nuget.config file.");
}
}

View File

@@ -18,9 +18,15 @@ namespace Semmle.Extraction.CSharp.Standalone
private const string aspNetCoreApp = "Microsoft.AspNetCore.App";
private readonly IDotNet dotNet;
private readonly Lazy<Dictionary<string, RuntimeVersion>> newestRuntimes;
private Dictionary<string, RuntimeVersion> NewestRuntimes => newestRuntimes.Value;
private static string ExecutingRuntime => RuntimeEnvironment.GetRuntimeDirectory();
public Runtime(IDotNet dotNet) => this.dotNet = dotNet;
public Runtime(IDotNet dotNet)
{
this.dotNet = dotNet;
this.newestRuntimes = new(GetNewestRuntimes);
}
internal record RuntimeVersion : IComparable<RuntimeVersion>
{
@@ -74,7 +80,7 @@ namespace Semmle.Extraction.CSharp.Standalone
public override string ToString() => FullPath;
}
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(\S+)\]$")]
[GeneratedRegex(@"^(\S+)\s(\d+\.\d+\.\d+)(-([a-z]+)\.(\d+\.\d+\.\d+))?\s\[(.+)\]$")]
private static partial Regex RuntimeRegex();
/// <summary>
@@ -140,33 +146,42 @@ namespace Semmle.Extraction.CSharp.Standalone
}
}
private IEnumerable<string> GetRuntimes()
/// <summary>
/// Gets the .NET runtime location to use for extraction.
/// </summary>
public string GetRuntime(bool useSelfContained)
{
// Gets the newest version of the installed runtimes.
var newestRuntimes = GetNewestRuntimes();
if (useSelfContained)
{
return ExecutingRuntime;
}
// Location of the newest .NET Core Runtime.
if (newestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion))
if (NewestRuntimes.TryGetValue(netCoreApp, out var netCoreVersion))
{
yield return netCoreVersion.FullPath;
return netCoreVersion.FullPath;
}
// Location of the newest ASP.NET Core Runtime.
if (newestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion))
if (DesktopRuntimes.Any())
{
yield return aspNetCoreVersion.FullPath;
return DesktopRuntimes.First();
}
foreach (var r in DesktopRuntimes)
yield return r;
// A bad choice if it's the self-contained runtime distributed in codeql dist.
yield return ExecutingRuntime;
return ExecutingRuntime;
}
/// <summary>
/// Gets the .NET runtime location to use for extraction
/// Gets the ASP.NET runtime location to use for extraction, if one exists.
/// </summary>
public string GetRuntime(bool useSelfContained) => useSelfContained ? ExecutingRuntime : GetRuntimes().First();
public string? GetAspRuntime()
{
// Location of the newest ASP.NET Core Runtime.
if (NewestRuntimes.TryGetValue(aspNetCoreApp, out var aspNetCoreVersion))
{
return aspNetCoreVersion.FullPath;
}
return null;
}
}
}

View File

@@ -0,0 +1,95 @@
using Xunit;
using Semmle.BuildAnalyser;
using Semmle.Util.Logging;
using System.Collections.Generic;
namespace Semmle.Extraction.Tests
{
internal class LoggerStub : ILogger
{
public void Log(Severity severity, string message) { }
public void Dispose() { }
}
internal class UnsafeFileReaderStub : IUnsafeFileReader
{
private readonly List<string> lines;
public UnsafeFileReaderStub(List<string> lines)
{
this.lines = lines;
}
public IEnumerable<string> ReadLines(string file)
{
foreach (var line in lines)
{
yield return line;
}
}
}
internal class TestFileContent : FileContent
{
public TestFileContent(List<string> lines) : base(() => new HashSet<string>(),
new ProgressMonitor(new LoggerStub()),
() => new List<string>() { "test1.cs" },
new UnsafeFileReaderStub(lines))
{ }
}
public class FileContentTests
{
[Fact]
public void TestFileContent1()
{
// Setup
var lines = new List<string>()
{
"<Project Sdk=\"Microsoft.NET.Sdk\">",
"<PackageReference Include=\"DotNetAnalyzers.DocumentationAnalyzers\" Version=\"1.0.0-beta.59\" PrivateAssets=\"all\" />",
"<PackageReference Version=\"7.0.0\" Include=\"Microsoft.CodeAnalysis.NetAnalyzers\"PrivateAssets=\"all\" />",
"<PackageReference Include=\"StyleCop.Analyzers\" Version=\"1.2.0-beta.406\">",
"<FrameworkReference Include=\"My.Framework\"/>"
};
var fileContent = new TestFileContent(lines);
// Execute
var notYetDownloadedPackages = fileContent.NotYetDownloadedPackages;
var useAspNetDlls = fileContent.UseAspNetDlls;
// Verify
Assert.False(useAspNetDlls);
Assert.Equal(3, notYetDownloadedPackages.Count);
Assert.Contains("DotNetAnalyzers.DocumentationAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
Assert.Contains("StyleCop.Analyzers".ToLowerInvariant(), notYetDownloadedPackages);
}
[Fact]
public void TestFileContent2()
{
// Setup
var lines = new List<string>()
{
"<Project Sdk=\"Microsoft.NET.Sdk.Web\">",
"<FrameworkReference Include=\"My.Framework\"/>",
"<PackageReference Version=\"7.0.0\" Include=\"Microsoft.CodeAnalysis.NetAnalyzers\"PrivateAssets=\"all\" />",
"<PackageReference Include=\"StyleCop.Analyzers\" Version=\"1.2.0-beta.406\">"
};
var fileContent = new TestFileContent(lines);
// Execute
var useAspNetDlls = fileContent.UseAspNetDlls;
var notYetDownloadedPackages = fileContent.NotYetDownloadedPackages;
// Verify
Assert.True(useAspNetDlls);
Assert.Equal(2, notYetDownloadedPackages.Count);
Assert.Contains("Microsoft.CodeAnalysis.NetAnalyzers".ToLowerInvariant(), notYetDownloadedPackages);
Assert.Contains("StyleCop.Analyzers".ToLowerInvariant(), notYetDownloadedPackages);
}
}
}

View File

@@ -100,5 +100,36 @@ namespace Semmle.Extraction.Tests
Assert.Equal("/path/dotnet/shared/Microsoft.NETCore.App/8.0.0-rc.4.43280.8", FixExpectedPathOnWindows(netCoreApp.FullPath));
}
[Fact]
public void TestRuntime4()
{
// Setup
var listedRuntimes = new List<string>
{
@"Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
@"Microsoft.AspNetCore.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
@"Microsoft.AspNetCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]",
@"Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
@"Microsoft.NETCore.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
@"Microsoft.NETCore.App 7.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]",
@"Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]",
@"Microsoft.WindowsDesktop.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]",
@"Microsoft.WindowsDesktop.App 7.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]"
};
var dotnet = new DotNetStub(listedRuntimes);
var runtime = new Runtime(dotnet);
// Execute
var runtimes = runtime.GetNewestRuntimes();
// Verify
Assert.Equal(3, runtimes.Count);
Assert.True(runtimes.TryGetValue("Microsoft.AspNetCore.App", out var aspNetCoreApp));
Assert.Equal(@"C:/Program Files/dotnet/shared/Microsoft.AspNetCore.App/7.0.2", FixExpectedPathOnWindows(aspNetCoreApp.FullPath));
Assert.True(runtimes.TryGetValue("Microsoft.NETCore.App", out var netCoreApp));
Assert.Equal(@"C:/Program Files/dotnet/shared/Microsoft.NETCore.App/7.0.2", FixExpectedPathOnWindows(netCoreApp.FullPath));
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
namespace Semmle.Util
{
/// <summary>
/// An instance of this class is used to ensure that the provided
/// action is executed only once and on the first call to `Run`.
/// It is thread-safe.
/// </summary>
public class Initializer
{
private readonly Lazy<bool> doInit;
public Initializer(Action action)
{
doInit = new Lazy<bool>(() =>
{
action();
return true;
});
}
public void Run()
{
var _ = doInit.Value;
}
}
}

View File

@@ -0,0 +1 @@
| Program.cs:0:0:0:0 | Program.cs |

View File

@@ -0,0 +1,5 @@
import csharp
from File f
where f.fromSource()
select f

View File

@@ -0,0 +1 @@
var dummy = "dummy";

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<Target Name="DeleteBinObjFolders" BeforeTargets="Clean">
<RemoveDir Directories=".\bin" />
<RemoveDir Directories=".\obj" />
</Target>
</Project>

View File

@@ -0,0 +1,3 @@
from create_database_utils import *
run_codeql_database_create([], lang="csharp", extra_args=["--extractor-option=buildless=true", "--extractor-option=cil=false"])

View File

@@ -397,6 +397,8 @@ The following components are supported:
- **SyntheticGlobal[**\ `name`\ **]** selects the synthetic global with name `name`.
- **ArrayElement** selects the elements of an array.
- **Element** selects the elements of a collection-like container.
- **WithoutElement** selects a collection-like container without its elements. This is for input only.
- **WithElement** selects the elements of a collection-like container, but points to the container itself. This is for input only.
- **MapKey** selects the element keys of a map.
- **MapValue** selects the element values of a map.

View File

@@ -122,12 +122,19 @@ Global environments
The global module environment has a single entry ``QlBuiltins``.
The global type environment has entries for the primitive types ``int``, ``float``, ``string``, ``boolean``, and ``date``, as well as any types defined in the database schema.
The global type environment has entries for the primitive types ``int``, ``float``, ``string``, ``boolean``, and ``date``.
The global predicate environment includes all the built-in classless predicates, as well as any extensional predicates declared in the database schema.
The global predicate environment includes all the built-in classless predicates.
The three global signature environments are empty.
Database schema environments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The database schema type environment has entries for types declared in the database schema.
The database schema predicate environment has entries for extensional predicates declared in the database schema.
The program is invalid if any of these environments is not definite.
Module environments
@@ -146,7 +153,7 @@ These are defined as follows (with X denoting the type of entity we are currentl
2. for each module which the current module directly imports (excluding ``private`` imports - see "`Import directives <#import-directives>`__"): all entries from the *exported X environment* that have a key not present in the *publically declared X environment* of the current module, and
3. if X is ``predicates``, then for each module signature ``S`` that is implemented by the current module: an entry for each module signature default predicate in ``S`` that does not have the same name and arity as any of the entries in the **publically declared predicate environment** of the current module.
3. if X is ``predicate``, then for each module signature ``S`` that is implemented by the current module: an entry for each module signature default predicate in ``S`` that does not have the same name and arity as any of the entries in the **publically declared predicate environment** of the current module.
- The *visible X environment* of a module is the union of
@@ -160,7 +167,9 @@ These are defined as follows (with X denoting the type of entity we are currentl
5. if there is an enclosing module: all entries from the *visible X environment* of the enclosing module that have a key not present in the *publically declared X environment* of the current module, and
6. all parameters of the current module that are of type X.
6. if there is no enclosing module and X is either ``type`` or ``predicate``: all entries from the *database schema X environment* that have a key not present in the *publically declared X environment* of the current module, and
7. all parameters of the current module that are of type X.
The program is invalid if any of these environments is not definite.

View File

@@ -773,7 +773,7 @@ func installDependenciesAndBuild() {
goModVersion, goModVersionFound := tryReadGoDirective(buildInfo)
if goModVersionFound && semver.Compare("v"+goModVersion, getEnvGoSemVer()) >= 0 {
if goModVersionFound && semver.Compare("v"+goModVersion, getEnvGoSemVer()) > 0 {
diagnostics.EmitNewerGoVersionNeeded()
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Add support for `WithElement` and `WithoutElement` for MaD access paths.

View File

@@ -140,7 +140,8 @@ extensions:
- ["java.util", "LinkedHashSet", False, "LinkedHashSet", "(Collection)", "", "Argument[0].Element", "Argument[this].Element", "value", "manual"]
- ["java.util", "LinkedList", False, "LinkedList", "(Collection)", "", "Argument[0].Element", "Argument[this].Element", "value", "manual"]
- ["java.util", "List", True, "add", "(int,Object)", "", "Argument[1]", "Argument[this].Element", "value", "manual"]
- ["java.util", "List", True, "addAll", "(int,Collection)", "", "Argument[1].Element", "Argument[this].Element", "value", "manual"]
- ["java.util", "List", True, "addAll", "(int,Collection)", "", "Argument[1].WithElement", "Argument[this]", "value", "manual"]
- ["java.util", "List", True, "clear", "()", "", "Argument[this].WithoutElement", "Argument[this]", "value", "manual"]
- ["java.util", "List", False, "copyOf", "(Collection)", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"]
- ["java.util", "List", True, "get", "(int)", "", "Argument[this].Element", "ReturnValue", "value", "manual"]
- ["java.util", "List", True, "listIterator", "", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"]
@@ -313,6 +314,7 @@ extensions:
- ["java.util", "Scanner", True, "useLocale", "", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["java.util", "Scanner", True, "useRadix", "", "", "Argument[this]", "ReturnValue", "value", "manual"]
- ["java.util", "Set", False, "copyOf", "(Collection)", "", "Argument[0].Element", "ReturnValue.Element", "value", "manual"]
- ["java.util", "Set", False, "clear", "()", "", "Argument[this].WithoutElement", "Argument[this]", "value", "manual"]
- ["java.util", "Set", False, "of", "(Object)", "", "Argument[0]", "ReturnValue.Element", "value", "manual"]
- ["java.util", "Set", False, "of", "(Object,Object)", "", "Argument[0..1]", "ReturnValue.Element", "value", "manual"]
- ["java.util", "Set", False, "of", "(Object,Object,Object)", "", "Argument[0..2]", "ReturnValue.Element", "value", "manual"]
@@ -424,10 +426,8 @@ extensions:
# When `WithoutElement` is implemented, these should be changed to summary models of the form `Argument[this].WithoutElement -> Argument[this]`.
- ["java.util", "Collection", "removeIf", "(Predicate)", "summary", "manual"]
- ["java.util", "Iterator", "remove", "()", "summary", "manual"]
- ["java.util", "List", "clear", "()", "summary", "manual"]
- ["java.util", "List", "remove", "(Object)", "summary", "manual"]
- ["java.util", "Map", "clear", "()", "summary", "manual"]
- ["java.util", "Set", "clear", "()", "summary", "manual"]
- ["java.util", "Set", "remove", "(Object)", "summary", "manual"]
- ["java.util", "Set", "removeAll", "(Collection)", "summary", "manual"]

View File

@@ -35,8 +35,9 @@
* or method, or a parameter.
* 7. The `input` column specifies how data enters the element selected by the
* first 6 columns, and the `output` column specifies how data leaves the
* element selected by the first 6 columns. An `input` can be either "",
* "Argument[n]", "Argument[n1..n2]", "ReturnValue":
* element selected by the first 6 columns. An `input` can be a dot separated
* path consisting of either "", "Argument[n]", "Argument[n1..n2]",
* "ReturnValue", "Element", "WithoutElement", or "WithElement":
* - "": Selects a write to the selected element in case this is a field.
* - "Argument[n]": Selects an argument in a call to the selected element.
* The arguments are zero-indexed, and `this` specifies the qualifier.
@@ -44,9 +45,15 @@
* the given range. The range is inclusive at both ends.
* - "ReturnValue": Selects a value being returned by the selected element.
* This requires that the selected element is a method with a body.
* - "Element": Selects the collection elements of the selected element.
* - "WithoutElement": Selects the selected element but without
* its collection elements.
* - "WithElement": Selects the collection elements of the selected element, but
* points to the selected element.
*
* An `output` can be either "", "Argument[n]", "Argument[n1..n2]", "Parameter",
* "Parameter[n]", "Parameter[n1..n2]", or "ReturnValue":
* An `output` can be can be a dot separated path consisting of either "",
* "Argument[n]", "Argument[n1..n2]", "Parameter", "Parameter[n]",
* "Parameter[n1..n2]", "ReturnValue", or "Element":
* - "": Selects a read of a selected field, or a selected parameter.
* - "Argument[n]": Selects the post-update value of an argument in a call to the
* selected element. That is, the value of the argument after the call returns.
@@ -61,6 +68,7 @@
* - "Parameter[n1..n2]": Similar to "Parameter[n]" but selects any parameter
* in the given range. The range is inclusive at both ends.
* - "ReturnValue": Selects the return value of a call to the selected element.
* - "Element": Selects the collection elements of the selected element.
* 8. The `kind` column is a tag that can be referenced from QL to determine to
* which classes the interpreted elements should be added. For example, for
* sources "remote" indicates a default remote flow source, and for summaries

View File

@@ -170,6 +170,10 @@ predicate neutralSummaryElement(SummarizedCallableBase c, string provenance) {
bindingset[c]
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
exists(Content content | parseContent(c, content) and result = SummaryComponent::content(content))
or
c = "WithoutElement" and result = SummaryComponent::withoutContent(any(CollectionContent cc))
or
c = "WithElement" and result = SummaryComponent::withContent(any(CollectionContent cc))
}
/** Gets the summary component for specification component `c`, if any. */
@@ -196,6 +200,10 @@ private string getContentSpecific(Content c) {
/** Gets the textual representation of the content in the format used for MaD models. */
string getMadRepresentationSpecific(SummaryComponent sc) {
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c))
or
sc = TWithoutContentSummaryComponent(_) and result = "WithoutElement"
or
sc = TWithContentSummaryComponent(_) and result = "WithElement"
}
bindingset[pos]

View File

@@ -21,21 +21,97 @@ import AutomodelEndpointTypes as AutomodelEndpointTypes
newtype JavaRelatedLocationType = CallContext()
newtype TApplicationModeEndpoint =
TExplicitArgument(Call call, DataFlow::Node arg) {
exists(Argument argExpr |
arg.asExpr() = argExpr and call = argExpr.getCall() and not argExpr.isVararg()
)
} or
TInstanceArgument(Call call, DataFlow::Node arg) { arg = DataFlow::getInstanceArgument(call) } or
TImplicitVarargsArray(Call call, DataFlow::Node arg, int idx) {
exists(Argument argExpr |
arg.asExpr() = argExpr and
call.getArgument(idx) = argExpr and
argExpr.isVararg() and
not exists(int i | i < idx and call.getArgument(i).(Argument).isVararg())
)
}
/**
* An endpoint is a node that is a candidate for modeling.
*/
abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
abstract predicate isArgOf(Call c, int idx);
Call getCall() { this.isArgOf(result, _) }
int getArgIndex() { this.isArgOf(_, result) }
abstract Top asTop();
abstract DataFlow::Node asNode();
abstract string toString();
}
/**
* A class representing nodes that are arguments to calls.
*/
private class ArgumentNode extends DataFlow::Node {
Call c;
class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument {
Call call;
DataFlow::Node arg;
ArgumentNode() {
exists(Argument arg | this.asExpr() = arg and not arg.isVararg() and c = arg.getCall())
or
this.(DataFlow::ImplicitVarargsArray).getCall() = c
or
this = DataFlow::getInstanceArgument(c)
ExplicitArgument() { this = TExplicitArgument(call, arg) }
override predicate isArgOf(Call c, int idx) { c = call and this.asTop() = c.getArgument(idx) }
override Top asTop() { result = arg.asExpr() }
override DataFlow::Node asNode() { result = arg }
override string toString() { result = arg.toString() }
}
class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
Call call;
DataFlow::Node arg;
InstanceArgument() { this = TInstanceArgument(call, arg) }
override predicate isArgOf(Call c, int idx) {
c = call and this.asTop() = c.getQualifier() and idx = -1
}
Call getCall() { result = c }
override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
override DataFlow::Node asNode() { result = arg }
override string toString() { result = arg.toString() }
}
/**
* An endpoint that represents an implicit varargs array.
* We choose to represent the varargs array as a single endpoint, rather than as multiple endpoints.
*
* This avoids the problem of having to deal with redundant endpoints downstream.
*
* In order to be able to distinguish between varargs endpoints and regular endpoints, we export the `isVarargsArray`
* meta data field in the extraction queries.
*/
class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArray {
Call call;
DataFlow::Node vararg;
int idx;
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, vararg, idx) }
override predicate isArgOf(Call c, int i) { c = call and i = idx }
override Top asTop() { result = this.getCall() }
override DataFlow::Node asNode() { result = vararg }
override string toString() { result = vararg.toString() }
}
/**
@@ -47,7 +123,7 @@ private class ArgumentNode extends DataFlow::Node {
*/
module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig {
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
class Endpoint = ArgumentNode;
class Endpoint = ApplicationModeEndpoint;
class EndpointType = AutomodelEndpointTypes::EndpointType;
@@ -61,18 +137,18 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
predicate isSanitizer(Endpoint e, EndpointType t) {
exists(t) and
(
e.getType() instanceof BoxedType
e.asNode().getType() instanceof BoxedType
or
e.getType() instanceof PrimitiveType
e.asNode().getType() instanceof PrimitiveType
or
e.getType() instanceof NumberType
e.asNode().getType() instanceof NumberType
)
or
t instanceof AutomodelEndpointTypes::PathInjectionSinkType and
e instanceof PathSanitizer::PathInjectionSanitizer
e.asNode() instanceof PathSanitizer::PathInjectionSanitizer
}
RelatedLocation asLocation(Endpoint e) { result = e.asExpr() }
RelatedLocation asLocation(Endpoint e) { result = e.asTop() }
predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
@@ -98,16 +174,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
ApplicationModeGetCallable::getCallable(e).hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(ApplicationModeGetCallable::getCallable(e)) and
ext = "" and
(
exists(Call c, int argIdx |
e.asExpr() = c.getArgument(argIdx) and
input = AutomodelJavaUtil::getArgumentForIndex(argIdx)
)
or
exists(Call c |
e.asExpr() = c.getQualifier() and input = AutomodelJavaUtil::getArgumentForIndex(-1)
)
)
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex())
}
/**
@@ -118,7 +185,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
*/
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
type = CallContext() and
result = any(Call c | e.asExpr() = [c.getAnArgument(), c.getQualifier()])
result = e.getCall()
}
}
@@ -132,12 +199,7 @@ private module ApplicationModeGetCallable implements AutomodelSharedGetCallable:
/**
* Returns the API callable being modeled.
*/
Callable getCallable(Endpoint e) {
exists(Call c |
e.asExpr() = [c.getAnArgument(), c.getQualifier()] and
result = c.getCallee()
)
}
Callable getCallable(Endpoint e) { result = e.getCall().getCallee() }
}
/**
@@ -145,7 +207,7 @@ private module ApplicationModeGetCallable implements AutomodelSharedGetCallable:
* should be empty.
*/
private predicate isCustomSink(Endpoint e, string kind) {
e instanceof QueryInjectionSink and kind = "sql"
e.asNode() instanceof QueryInjectionSink and kind = "sql"
}
module CharacteristicsImpl =
@@ -167,23 +229,21 @@ class ApplicationModeMetadataExtractor extends string {
predicate hasMetadata(
Endpoint e, string package, string type, string subtypes, string name, string signature,
string input
string input, string isVarargsArray
) {
exists(Call call, Callable callable, int argIdx |
call.getCallee() = callable and
(
e.asExpr() = call.getArgument(argIdx)
or
e.asExpr() = call.getQualifier() and argIdx = -1
) and
input = AutomodelJavaUtil::getArgumentForIndex(argIdx) and
exists(Callable callable |
e.getCall().getCallee() = callable and
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex()) and
package = callable.getDeclaringType().getPackage().getName() and
// we're using the erased types because the MaD convention is to not specify type parameters.
// Whether something is or isn't a sink doesn't usually depend on the type parameters.
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and
name = callable.getName() and
signature = ExternalFlow::paramsString(callable)
signature = ExternalFlow::paramsString(callable) and
if e instanceof ImplicitVarargsArray
then isVarargsArray = "true"
else isVarargsArray = "false"
)
}
}
@@ -253,28 +313,10 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
IsMaDTaintStepCharacteristic() { this = "taint step" }
override predicate appliesToEndpoint(Endpoint e) {
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e, _, _) or
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e, _, _) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(e, _, _, _) or
FlowSummaryImpl::Private::Steps::summarySetterStep(e, _, _, _)
}
}
/**
* A negative characteristic that filters out qualifiers that are classes (i.e. static calls). These
* are unlikely to have any non-trivial flow going into them.
*
* Technically, an accessed type _could_ come from outside of the source code, but there's not
* much likelihood of that being user-controlled.
*/
private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
ClassQualifierCharacteristic() { this = "class qualifier" }
override predicate appliesToEndpoint(Endpoint e) {
exists(Call c |
e.asExpr() = c.getQualifier() and
c.getQualifier() instanceof TypeAccess
)
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _) or
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _) or
FlowSummaryImpl::Private::Steps::summarySetterStep(e.asNode(), _, _, _)
}
}
@@ -351,7 +393,7 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
FunctionValueCharacteristic() { this = "function value" }
override predicate appliesToEndpoint(Endpoint e) { e.asExpr() instanceof FunctionalExpr }
override predicate appliesToEndpoint(Endpoint e) { e.asNode().asExpr() instanceof FunctionalExpr }
}
/**
@@ -371,12 +413,12 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN
* Holds if the node `n` is known as the predecessor in a modeled flow step.
*/
private predicate isKnownOutNodeForStep(Endpoint e) {
e.asExpr() instanceof Call or // we just assume flow in that case
TaintTracking::localTaintStep(_, e) or
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e, _) or
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e, _) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e, _) or
FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e, _)
e.asNode().asExpr() instanceof Call or // we just assume flow in that case
TaintTracking::localTaintStep(_, e.asNode()) or
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e.asNode(), _) or
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e.asNode(), _) or
FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e.asNode(), _) or
FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e.asNode(), _)
}
}

View File

@@ -25,16 +25,18 @@ private import AutomodelJavaUtil
bindingset[limit]
private Endpoint getSampleForSignature(
int limit, string package, string type, string subtypes, string name, string signature,
string input
string input, string isVarargs
) {
exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta |
num_endpoints =
count(Endpoint e | meta.hasMetadata(e, package, type, subtypes, name, signature, input))
count(Endpoint e |
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
)
|
result =
rank[n](Endpoint e, Location loc |
loc = e.getLocation() and
meta.hasMetadata(e, package, type, subtypes, name, signature, input)
loc = e.asTop().getLocation() and
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
|
e
order by
@@ -53,19 +55,20 @@ private Endpoint getSampleForSignature(
from
Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, DollarAtString package,
DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
DollarAtString input
DollarAtString input, DollarAtString isVarargsArray
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
) and
endpoint = getSampleForSignature(9, package, type, subtypes, name, signature, input) and
endpoint =
getSampleForSignature(9, package, type, subtypes, name, signature, input, isVarargsArray) and
// If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
// don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
// label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
// overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
not CharacteristicsImpl::isSink(endpoint, _, _) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
includeAutomodelCandidate(package, type, name, signature) and
// The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
// a non-sink, and we surface only endpoints that have at least one such sink type.
@@ -76,11 +79,13 @@ where
|
sinkType, ", "
)
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
select endpoint.asNode(),
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
package, "package", //
type, "type", //
subtypes, "subtypes", //
name, "name", // method name
signature, "signature", //
input, "input" //
input, "input", //
isVarargsArray, "isVarargsArray"

View File

@@ -24,7 +24,7 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
exists(int n, int num_endpoints | num_endpoints = count(Endpoint e | c.appliesToEndpoint(e)) |
result =
rank[n](Endpoint e, Location loc |
loc = e.getLocation() and c.appliesToEndpoint(e)
loc = e.asTop().getLocation() and c.appliesToEndpoint(e)
|
e
order by
@@ -43,7 +43,8 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
from
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type,
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
DollarAtString isVarargsArray
where
endpoint = getSampleForCharacteristic(characteristic, 100) and
confidence >= SharedCharacteristics::highConfidence() and
@@ -51,7 +52,7 @@ where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
not erroneousEndpoints(endpoint, _, _, _, _, false) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
// treated by the actual query as a sanitizer, since the final logic is something like
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
@@ -63,11 +64,13 @@ where
characteristic2.hasImplications(positiveType, true, confidence2)
) and
message = characteristic
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
select endpoint.asNode(),
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
package, "package", //
type, "type", //
subtypes, "subtypes", //
name, "name", //
signature, "signature", //
input, "input" //
input, "input", //
isVarargsArray, "isVarargsArray" //

View File

@@ -15,19 +15,22 @@ private import AutomodelJavaUtil
from
Endpoint endpoint, SinkType sinkType, ApplicationModeMetadataExtractor meta,
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
DollarAtString signature, DollarAtString input
DollarAtString signature, DollarAtString input, DollarAtString isVarargsArray
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
not erroneousEndpoints(endpoint, _, _, _, _, false) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
// Extract positive examples of sinks belonging to the existing ATM query configurations.
CharacteristicsImpl::isKnownSink(endpoint, sinkType)
select endpoint, sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::isKnownSink(endpoint, sinkType) and
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
select endpoint.asNode(),
sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
package, "package", //
type, "type", //
subtypes, "subtypes", //
name, "name", //
signature, "signature", //
input, "input" //
input, "input", //
isVarargsArray, "isVarargsArray"

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@
| java.time | 0 | 0 | 0 | 17 | 17 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
| java.time.chrono | 0 | 0 | 0 | 1 | 1 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
| java.time.format | 0 | 0 | 0 | 2 | 2 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |
| java.util | 0 | 0 | 84 | 68 | 152 | 0.5526315789473685 | 0.0 | 0.5526315789473685 | 0.0 | NaN | 0.4473684210526316 |
| java.util | 0 | 0 | 86 | 66 | 152 | 0.5657894736842105 | 0.0 | 0.5657894736842105 | 0.0 | NaN | 0.4342105263157895 |
| java.util.concurrent | 0 | 0 | 9 | 9 | 18 | 0.5 | 0.0 | 0.5 | 0.0 | NaN | 0.5 |
| java.util.concurrent.atomic | 0 | 0 | 2 | 11 | 13 | 0.15384615384615385 | 0.0 | 0.15384615384615385 | 0.0 | NaN | 0.8461538461538461 |
| java.util.concurrent.locks | 0 | 0 | 0 | 2 | 2 | 0.0 | 0.0 | 0.0 | NaN | NaN | 1.0 |

View File

@@ -1,2 +1,3 @@
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input |
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:53:4:53:4 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://true:1:1:1:1 | true | isVarargsArray |

View File

@@ -1,2 +1,2 @@
| Test.java:40:14:40:21 | openPath | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:40:4:40:22 | get(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Paths:1:1:1:1 | Paths | type | file://false:1:1:1:1 | false | subtypes | file://get:1:1:1:1 | get | name | file://(String,String[]):1:1:1:1 | (String,String[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:52:4:52:4 | p | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |

View File

@@ -1,3 +1,3 @@
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input |
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input |
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://false:1:1:1:1 | false | isVarargsArray |
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |

View File

@@ -8,7 +8,7 @@ import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.io.File;
import java.nio.file.FileVisitOption;
class Test {
public static void main(String[] args) throws Exception {
@@ -46,5 +46,14 @@ class Test {
f2 // negative example (modeled as not a sink)
);
}
public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception {
Files.walk(
p, // negative example (modeled as a taint step)
o, // the implicit varargs array is a candidate
o // not a candidate (only the first arg corresponding to a varargs array
// is extracted)
);
}
}

View File

@@ -445,7 +445,7 @@
| tst.js:146:15:146:21 | (\\d\|5)* | Strings with many repetitions of '0' can start matching anywhere after the start of the preceeding ((\\d\|5)*)" |
| tst.js:149:15:149:24 | (\\s\|[\\f])* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding ((\\s\|[\\f])*)" |
| tst.js:152:15:152:28 | (\\s\|[\\v]\|\\\\v)* | Strings with many repetitions of '\\t' can start matching anywhere after the start of the preceeding ((\\s\|[\\v]\|\\\\v)*)" |
| tst.js:155:15:155:24 | (\\f\|[\\f])* | Strings with many repetitions of '\u000c' can start matching anywhere after the start of the preceeding ((\\f\|[\\f])*)" |
| tst.js:155:15:155:24 | (\\f\|[\\f])* | Strings with many repetitions of '\\u000c' can start matching anywhere after the start of the preceeding ((\\f\|[\\f])*)" |
| tst.js:158:15:158:22 | (\\W\|\\D)* | Strings with many repetitions of '/' can start matching anywhere after the start of the preceeding ((\\W\|\\D)*)" |
| tst.js:161:15:161:22 | (\\S\|\\w)* | Strings with many repetitions of '!' can start matching anywhere after the start of the preceeding ((\\S\|\\w)*)" |
| tst.js:164:15:164:24 | (\\S\|[\\w])* | Strings with many repetitions of '!' can start matching anywhere after the start of the preceeding ((\\S\|[\\w])*)" |

View File

@@ -123,9 +123,9 @@
| tst.js:137:15:137:21 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. |
| tst.js:143:15:143:22 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| tst.js:146:15:146:21 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. |
| tst.js:149:15:149:24 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
| tst.js:152:15:152:28 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000b'. |
| tst.js:155:15:155:24 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
| tst.js:149:15:149:24 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
| tst.js:152:15:152:28 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000b'. |
| tst.js:155:15:155:24 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
| tst.js:158:15:158:22 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '/'. |
| tst.js:161:15:161:22 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| tst.js:164:15:164:24 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
@@ -199,3 +199,5 @@
| tst.js:404:6:405:7 | (g\|gg)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'gg'. |
| tst.js:407:125:407:127 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with '0/*' and containing many repetitions of ' ;0'. |
| tst.js:411:15:411:19 | a{1,} | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
| tst.js:413:25:413:35 | (\\u0000\|.)+ | This part of the regular expression may cause exponential backtracking on strings starting with '\\n\\u0000' and containing many repetitions of '\\u0000'. |
| tst.js:415:44:415:57 | (\ud83d\ude80\|.)+ | This part of the regular expression may cause exponential backtracking on strings starting with '\\n\\u{1f680}' and containing many repetitions of '\\u{1f680}'. |

View File

@@ -408,4 +408,8 @@ var bad98 = /^(?:\*\/\*|[a-zA-Z0-9][a-zA-Z0-9!\#\$&\-\^_\.\+]{0,126}\/(?:\*|[a-z
var good48 = /(\/(?:\/[\w.-]*)*){0,1}:([\w.-]+)/;
var bad99 = /(a{1,})*b/;
var bad99 = /(a{1,})*b/;
var unicode = /^\n\u0000(\u0000|.)+$/;
var largeUnicode = new RegExp("^\n\u{1F680}(\u{1F680}|.)+X$");

View File

@@ -51,6 +51,12 @@ def _get_includes(includes):
def _cmake_aspect_impl(target, ctx):
if not ctx.rule.kind.startswith("cc_"):
return [CmakeInfo(name = None, transitive_deps = depset())]
if ctx.rule.kind == "cc_binary_add_features":
dep = ctx.rule.attr.dep[0][CmakeInfo]
return [CmakeInfo(
name = None,
transitive_deps = depset([dep], transitive = [dep.transitive_deps]),
)]
name = _cmake_name(ctx.label)
@@ -146,7 +152,7 @@ def _cmake_aspect_impl(target, ctx):
cmake_aspect = aspect(
implementation = _cmake_aspect_impl,
attr_aspects = ["deps"],
attr_aspects = ["deps", "dep"],
fragments = ["cpp"],
)
@@ -156,10 +162,10 @@ def _map_cmake_info(info, is_windows):
"add_%s(%s)" % (info.kind, args),
]
if info.imported_libs:
commands += [
commands.append(
"target_link_libraries(%s %s %s)" %
(info.name, info.modifier or "PUBLIC", " ".join(info.imported_libs)),
]
)
if info.deps:
libs = {}
if info.modifier == "INTERFACE":
@@ -168,46 +174,46 @@ def _map_cmake_info(info, is_windows):
for lib in info.deps:
libs.setdefault(lib.modifier, []).append(lib.name)
for modifier, names in libs.items():
commands += [
commands.append(
"target_link_libraries(%s %s %s)" % (info.name, modifier or "PUBLIC", " ".join(names)),
]
)
if info.includes:
commands += [
commands.append(
"target_include_directories(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.includes)),
]
)
if info.system_includes:
commands += [
commands.append(
"target_include_directories(%s SYSTEM %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.system_includes)),
]
)
if info.quote_includes:
if is_windows:
commands += [
commands.append(
"target_include_directories(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.quote_includes)),
]
)
else:
commands += [
commands.append(
"target_compile_options(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(["-iquote%s" % i for i in info.quote_includes])),
]
)
if info.copts and info.modifier != "INTERFACE":
commands += [
commands.append(
"target_compile_options(%s PRIVATE %s)" % (info.name, " ".join(info.copts)),
]
)
if info.linkopts:
commands += [
commands.append(
"target_link_options(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.linkopts)),
]
)
if info.force_cxx_compilation and any([f.endswith(".c") for f in info.srcs]):
commands += [
commands.append(
"set_source_files_properties(%s PROPERTIES LANGUAGE CXX)" % " ".join([f for f in info.srcs if f.endswith(".c")]),
]
)
if info.defines:
commands += [
commands.append(
"target_compile_definitions(%s %s %s)" % (info.name, info.modifier or "PUBLIC", " ".join(info.defines)),
]
)
if info.local_defines:
commands += [
commands.append(
"target_compile_definitions(%s %s %s)" % (info.name, info.modifier or "PRIVATE", " ".join(info.local_defines)),
]
)
return commands
GeneratedCmakeFiles = provider(

View File

@@ -492,9 +492,14 @@ class NiceLocationExpr extends Expr {
// for `import xxx` or for `import xxx as yyy`.
this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec)
or
/* Show y for `y` in `from xxx import y` */
exists(string name |
name = this.(ImportMember).getName() and
// Show y for `y` in `from xxx import y`
// and y for `yyy as y` in `from xxx import yyy as y`.
exists(string name, Alias alias |
// This alias will always exist, as `from xxx import y`
// is expanded to `from xxx imprt y as y`.
this = alias.getValue() and
name = alias.getAsname().(Name).getId()
|
this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and
bl = el and
bc = ec - name.length() + 1

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Support analyzing packages (folders with python code) that do not have `__init__.py` files, although this is technically required, we see real world projects that don't have this.

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed the computation of locations for imports with aliases in jump-to-definition.

View File

@@ -195,7 +195,22 @@ private predicate isPotentialPackage(Folder f) {
}
private string moduleNameFromBase(Container file) {
isPotentialPackage(file) and result = file.getBaseName()
// We used to also require `isPotentialPackage(f)` to hold in this case,
// but we saw modules not getting resolved because their folder did not
// contain an `__init__.py` file.
//
// This makes the folder not be a package but a namespace package instead.
// In most cases this is a mistake :| See following links for more details
// - https://dev.to/methane/don-t-omit-init-py-3hga
// - https://packaging.python.org/en/latest/guides/packaging-namespace-packages/
// - https://discuss.python.org/t/init-py-pep-420-and-iter-modules-confusion/9642
//
// It is possible that we can keep the original requirement on
// `isPotentialPackage(f)` here, but relax `isPotentialPackage` itself to allow
// for this behavior of missing `__init__.py` files. However, doing so involves
// cascading changes (for example to `moduleNameFromFile`), and was a more involved
// task than we wanted to take on.
result = file.getBaseName()
or
file instanceof File and result = file.getStem()
}

View File

@@ -17,6 +17,6 @@ Since PEP 420 was accepted in Python 3, this test is Python 3 only.
from foo.bar.a import afunc
from foo_explicit.bar.a import explicit_afunc
afunc() # $ MISSING: pt,tt=afunc
afunc() # $ pt,tt="foo/bar/a.py:afunc"
explicit_afunc() # $ pt,tt="foo_explicit/bar/a.py:explicit_afunc"

View File

@@ -0,0 +1,4 @@
from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider as rp_obj
from nova.api.openstack.placement.policies import allocation_candidate as \
policies

View File

@@ -0,0 +1,6 @@
| import_statements.py | 1 | 6 | 1 | 33 |
| import_statements.py | 1 | 42 | 1 | 53 |
| import_statements.py | 2 | 6 | 2 | 41 |
| import_statements.py | 2 | 71 | 2 | 76 |
| import_statements.py | 3 | 6 | 3 | 42 |
| import_statements.py | 4 | 5 | 4 | 12 |

View File

@@ -0,0 +1,6 @@
import python
import analysis.DefinitionTracking
from NiceLocationExpr expr, string f, int bl, int bc, int el, int ec
where expr.hasLocationInfo(f, bl, bc, el, ec)
select f, bl, bc, el, ec

View File

@@ -35,9 +35,9 @@
| redos.py:139:25:139:31 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. |
| redos.py:145:25:145:32 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| redos.py:148:25:148:31 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. |
| redos.py:151:25:151:34 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
| redos.py:154:25:154:38 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000b'. |
| redos.py:157:25:157:34 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
| redos.py:151:25:151:34 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
| redos.py:154:25:154:38 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000b'. |
| redos.py:157:25:157:34 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
| redos.py:160:25:160:32 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. |
| redos.py:163:25:163:32 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| redos.py:166:25:166:34 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
@@ -67,8 +67,8 @@
| redos.py:259:24:259:126 | (.thisisagoddamnlongstringforstresstestingthequery\|\\sthisisagoddamnlongstringforstresstestingthequery)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\tthisisagoddamnlongstringforstresstestingthequery'. |
| redos.py:262:24:262:87 | (thisisagoddamnlongstringforstresstestingthequery\|this\\w+query)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'thisisagoddamnlongstringforstresstestingthequery'. |
| redos.py:262:78:262:80 | \\w+ | This part of the regular expression may cause exponential backtracking on strings starting with 'this' and containing many repetitions of '0querythis'. |
| redos.py:268:28:268:39 | ([\ufffd\ufffd]\|[\ufffd\ufffd])* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\ufffd'. |
| redos.py:271:28:271:41 | ((\ufffd\|\ufffd)\|(\ufffd\|\ufffd))* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\ufffd'. |
| redos.py:268:28:268:39 | ([\ufffd\ufffd]\|[\ufffd\ufffd])* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\\ufffd'. |
| redos.py:271:28:271:41 | ((\ufffd\|\ufffd)\|(\ufffd\|\ufffd))* | This part of the regular expression may cause exponential backtracking on strings starting with 'foo' and containing many repetitions of '\\ufffd'. |
| redos.py:274:31:274:32 | b+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'b'. |
| redos.py:277:48:277:50 | \\s* | This part of the regular expression may cause exponential backtracking on strings starting with '<0\\t0=' and containing many repetitions of '""\\t0='. |
| redos.py:283:26:283:27 | a+ | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'a'. |
@@ -103,5 +103,5 @@
| redos.py:385:24:385:30 | (\\d\|0)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| redos.py:386:26:386:32 | (\\d\|0)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| redos.py:391:15:391:25 | (\\u0061\|a)* | This part of the regular expression may cause exponential backtracking on strings starting with 'X' and containing many repetitions of 'a'. |
| unittests.py:5:17:5:23 | (\u00c6\|\\\u00c6)+ | This part of the regular expression may cause exponential backtracking on strings starting with 'X' and containing many repetitions of '\u00c6'. |
| unittests.py:5:17:5:23 | (\u00c6\|\\\u00c6)+ | This part of the regular expression may cause exponential backtracking on strings starting with 'X' and containing many repetitions of '\\u00c6'. |
| unittests.py:9:16:9:24 | (?:.\|\\n)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\n'. |

View File

@@ -48,7 +48,7 @@ class UnaryLogicalOperation extends UnaryOperation, TUnaryLogicalOperation { }
* not params.empty?
* ```
*/
class NotExpr extends UnaryLogicalOperation, TNotExpr {
class NotExpr extends UnaryLogicalOperation instanceof NotExprImpl {
final override string getAPrimaryQlClass() { result = "NotExpr" }
}
@@ -118,7 +118,7 @@ class ComplementExpr extends UnaryBitwiseOperation, TComplementExpr {
* defined? some_method
* ```
*/
class DefinedExpr extends UnaryOperation, TDefinedExpr {
class DefinedExpr extends UnaryOperation instanceof DefinedExprImpl {
final override string getAPrimaryQlClass() { result = "DefinedExpr" }
}

View File

@@ -123,7 +123,8 @@ private module Cached {
TConstantWriteAccessSynth(Ast::AstNode parent, int i, string value) {
mkSynthChild(ConstantWriteAccessKind(value), parent, i)
} or
TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
TDefinedExprReal(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
TDefinedExprSynth(Ast::AstNode parent, int i) { mkSynthChild(DefinedExprKind(), parent, i) } or
TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or
TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) {
not strictcount(int i | exists(g.getParent().(Ruby::LeftAssignmentList).getChild(i))) = 1
@@ -228,7 +229,8 @@ private module Cached {
TNilLiteralReal(Ruby::Nil g) or
TNilLiteralSynth(Ast::AstNode parent, int i) { mkSynthChild(NilLiteralKind(), parent, i) } or
TNoRegExpMatchExpr(Ruby::Binary g) { g instanceof @ruby_binary_bangtilde } or
TNotExpr(Ruby::Unary g) { g instanceof @ruby_unary_bang or g instanceof @ruby_unary_not } or
TNotExprReal(Ruby::Unary g) { g instanceof @ruby_unary_bang or g instanceof @ruby_unary_not } or
TNotExprSynth(Ast::AstNode parent, int i) { mkSynthChild(NotExprKind(), parent, i) } or
TOptionalParameter(Ruby::OptionalParameter g) or
TPair(Ruby::Pair g) or
TParenthesizedExpr(Ruby::ParenthesizedStatements g) or
@@ -354,21 +356,21 @@ private module Cached {
TBitwiseOrExprReal or TBitwiseXorExprReal or TBlockArgument or TBlockParameter or
TBraceBlockReal or TBreakStmt or TCaseEqExpr or TCaseExpr or TCaseMatchReal or
TCharacterLiteral or TClassDeclaration or TClassVariableAccessReal or TComplementExpr or
TComplexLiteral or TDefinedExpr or TDelimitedSymbolLiteral or TDestructuredLeftAssignment or
TDestructuredParameter or TDivExprReal or TDo or TDoBlock or TElementReference or
TElseReal or TElsif or TEmptyStmt or TEncoding or TEndBlock or TEnsure or TEqExpr or
TExponentExprReal or TFalseLiteral or TFile or TFindPattern or TFloatLiteral or TForExpr or
TForwardParameter or TForwardArgument or TGEExpr or TGTExpr or TGlobalVariableAccessReal or
THashKeySymbolLiteral or THashLiteral or THashPattern or THashSplatExpr or
THashSplatNilParameter or THashSplatParameter or THereDoc or TIdentifierMethodCall or
TIfReal or TIfModifierExpr or TInClauseReal or TInstanceVariableAccessReal or
TIntegerLiteralReal or TKeywordParameter or TLEExpr or TLShiftExprReal or TLTExpr or
TLambda or TLeftAssignmentList or TLine or TLocalVariableAccessReal or
TLogicalAndExprReal or TLogicalOrExprReal or TMethod or TMatchPattern or
TModuleDeclaration or TModuloExprReal or TMulExprReal or TNEExpr or TNextStmt or
TNilLiteralReal or TNoRegExpMatchExpr or TNotExpr or TOptionalParameter or TPair or
TParenthesizedExpr or TParenthesizedPattern or TRShiftExprReal or TRangeLiteralReal or
TRationalLiteral or TRedoStmt or TRegExpLiteral or TRegExpMatchExpr or
TComplexLiteral or TDefinedExprReal or TDelimitedSymbolLiteral or
TDestructuredLeftAssignment or TDestructuredParameter or TDivExprReal or TDo or TDoBlock or
TElementReference or TElseReal or TElsif or TEmptyStmt or TEncoding or TEndBlock or
TEnsure or TEqExpr or TExponentExprReal or TFalseLiteral or TFile or TFindPattern or
TFloatLiteral or TForExpr or TForwardParameter or TForwardArgument or TGEExpr or TGTExpr or
TGlobalVariableAccessReal or THashKeySymbolLiteral or THashLiteral or THashPattern or
THashSplatExpr or THashSplatNilParameter or THashSplatParameter or THereDoc or
TIdentifierMethodCall or TIfReal or TIfModifierExpr or TInClauseReal or
TInstanceVariableAccessReal or TIntegerLiteralReal or TKeywordParameter or TLEExpr or
TLShiftExprReal or TLTExpr or TLambda or TLeftAssignmentList or TLine or
TLocalVariableAccessReal or TLogicalAndExprReal or TLogicalOrExprReal or TMethod or
TMatchPattern or TModuleDeclaration or TModuloExprReal or TMulExprReal or TNEExpr or
TNextStmt or TNilLiteralReal or TNoRegExpMatchExpr or TNotExprReal or TOptionalParameter or
TPair or TParenthesizedExpr or TParenthesizedPattern or TRShiftExprReal or
TRangeLiteralReal or TRationalLiteral or TRedoStmt or TRegExpLiteral or TRegExpMatchExpr or
TRegularArrayLiteral or TRegularMethodCall or TRegularStringLiteral or TRegularSuperCall or
TRescueClause or TRescueModifierExpr or TRetryStmt or TReturnStmt or
TScopeResolutionConstantAccess or TSelfReal or TSimpleParameterReal or
@@ -438,7 +440,7 @@ private module Cached {
n = TClassVariableAccessReal(result, _) or
n = TComplementExpr(result) or
n = TComplexLiteral(result) or
n = TDefinedExpr(result) or
n = TDefinedExprReal(result) or
n = TDelimitedSymbolLiteral(result) or
n = TDestructuredLeftAssignment(result) or
n = TDivExprReal(result) or
@@ -495,7 +497,7 @@ private module Cached {
n = TNextStmt(result) or
n = TNilLiteralReal(result) or
n = TNoRegExpMatchExpr(result) or
n = TNotExpr(result) or
n = TNotExprReal(result) or
n = TOptionalParameter(result) or
n = TPair(result) or
n = TParenthesizedExpr(result) or
@@ -585,6 +587,8 @@ private module Cached {
or
result = TConstantWriteAccessSynth(parent, i, _)
or
result = TDefinedExprSynth(parent, i)
or
result = TDivExprSynth(parent, i)
or
result = TElseSynth(parent, i)
@@ -617,6 +621,8 @@ private module Cached {
or
result = TNilLiteralSynth(parent, i)
or
result = TNotExprSynth(parent, i)
or
result = TRangeLiteralSynth(parent, i, _)
or
result = TRShiftExprSynth(parent, i)
@@ -789,10 +795,14 @@ class TNamespace = TClassDeclaration or TModuleDeclaration;
class TOperation = TUnaryOperation or TBinaryOperation or TAssignment;
class TDefinedExpr = TDefinedExprReal or TDefinedExprSynth;
class TUnaryOperation =
TUnaryLogicalOperation or TUnaryArithmeticOperation or TUnaryBitwiseOperation or TDefinedExpr or
TSplatExpr or THashSplatExpr;
class TNotExpr = TNotExprReal or TNotExprSynth;
class TUnaryLogicalOperation = TNotExpr;
class TUnaryArithmeticOperation = TUnaryPlusExpr or TUnaryMinusExpr;

View File

@@ -35,6 +35,16 @@ class UnaryOperationGenerated extends UnaryOperationImpl {
final override string getOperatorImpl() { result = g.getOperator() }
}
abstract class NotExprImpl extends UnaryOperationImpl, TNotExpr { }
class NotExprReal extends NotExprImpl, UnaryOperationGenerated, TNotExprReal { }
class NotExprSynth extends NotExprImpl, TNotExprSynth {
final override string getOperatorImpl() { result = "!" }
final override Expr getOperandImpl() { synthChild(this, 0, result) }
}
class SplatExprReal extends UnaryOperationImpl, TSplatExprReal {
private Ruby::SplatArgument g;
@@ -67,6 +77,16 @@ class HashSplatExprImpl extends UnaryOperationImpl, THashSplatExpr {
final override string getOperatorImpl() { result = "**" }
}
abstract class DefinedExprImpl extends UnaryOperationImpl, TDefinedExpr { }
class DefinedExprReal extends DefinedExprImpl, UnaryOperationGenerated, TDefinedExprReal { }
class DefinedExprSynth extends DefinedExprImpl, TDefinedExprSynth {
final override string getOperatorImpl() { result = "defined?" }
final override Expr getOperandImpl() { synthChild(this, 0, result) }
}
abstract class BinaryOperationImpl extends OperationImpl, MethodCallImpl, TBinaryOperation {
abstract Stmt getLeftOperandImpl();

View File

@@ -21,6 +21,7 @@ newtype SynthKind =
BraceBlockKind() or
CaseMatchKind() or
ClassVariableAccessKind(ClassVariable v) or
DefinedExprKind() or
DivExprKind() or
ElseKind() or
ExponentExprKind() or
@@ -40,6 +41,7 @@ newtype SynthKind =
ModuloExprKind() or
MulExprKind() or
NilLiteralKind() or
NotExprKind() or
RangeLiteralKind(boolean inclusive) { inclusive in [false, true] } or
RShiftExprKind() or
SimpleParameterKind() or
@@ -1258,6 +1260,7 @@ private module HashLiteralDesugar {
* ```
* desugars to, roughly,
* ```rb
* if not defined? x then x = nil end
* xs.each { |__synth__0| x = __synth__0; <loop_body> }
* ```
*
@@ -1267,58 +1270,160 @@ private module HashLiteralDesugar {
* scoped to the synthesized block.
*/
private module ForLoopDesugar {
private Ruby::AstNode getForLoopPatternChild(Ruby::For for) {
result = for.getPattern()
or
result.getParent() = getForLoopPatternChild(for)
}
/** Holds if `n` is an access to variable `v` in the pattern of `for`. */
pragma[nomagic]
private predicate forLoopVariableAccess(Ruby::For for, Ruby::AstNode n, VariableReal v) {
n = getForLoopPatternChild(for) and
access(n, v)
}
/** Holds if `v` is the `i`th iteration variable of `for`. */
private predicate forLoopVariable(Ruby::For for, VariableReal v, int i) {
v =
rank[i + 1](VariableReal v0, Ruby::AstNode n, Location l |
forLoopVariableAccess(for, n, v0) and
l = n.getLocation()
|
v0 order by l.getStartLine(), l.getStartColumn()
)
}
/** Gets the number of iteration variables of `for`. */
private int forLoopVariableCount(Ruby::For for) {
result = count(int j | forLoopVariable(for, _, j))
}
private Ruby::For toTsFor(ForExpr for) { for = TForExpr(result) }
/**
* Synthesizes an assignment
* ```rb
* if not defined? v then v = nil end
* ```
* anchored at index `rootIndex` of `root`.
*/
bindingset[root, rootIndex, v]
private predicate nilAssignUndefined(
AstNode root, int rootIndex, AstNode parent, int i, Child child, VariableReal v
) {
parent = root and
i = rootIndex and
child = SynthChild(IfKind())
or
exists(AstNode if_ | if_ = TIfSynth(root, rootIndex) |
parent = if_ and
i = 0 and
child = SynthChild(NotExprKind())
or
exists(AstNode not_ | not_ = TNotExprSynth(if_, 0) |
parent = not_ and
i = 0 and
child = SynthChild(DefinedExprKind())
or
parent = TDefinedExprSynth(not_, 0) and
i = 0 and
child = SynthChild(LocalVariableAccessRealKind(v))
)
or
parent = if_ and
i = 1 and
child = SynthChild(AssignExprKind())
or
parent = TAssignExprSynth(if_, 1) and
(
i = 0 and
child = SynthChild(LocalVariableAccessRealKind(v))
or
i = 1 and
child = SynthChild(NilLiteralKind())
)
)
}
pragma[nomagic]
private predicate forLoopSynthesis(AstNode parent, int i, Child child) {
exists(ForExpr for |
// each call
parent = for and
i = -1 and
child = SynthChild(MethodCallKind("each", false, 0))
child = SynthChild(StmtSequenceKind())
or
exists(MethodCall eachCall | eachCall = TMethodCallSynth(for, -1, "each", false, 0) |
// receiver
parent = eachCall and
i = 0 and
child = childRef(for.getValue()) // value is the Enumerable
exists(AstNode seq | seq = TStmtSequenceSynth(for, -1) |
exists(VariableReal v, int j | forLoopVariable(toTsFor(for), v, j) |
nilAssignUndefined(seq, j, parent, i, child, v)
)
or
parent = eachCall and
i = 1 and
child = SynthChild(BraceBlockKind())
or
exists(Block block | block = TBraceBlockSynth(eachCall, 1) |
// block params
parent = block and
i = 0 and
child = SynthChild(SimpleParameterKind())
exists(int numberOfVars | numberOfVars = forLoopVariableCount(toTsFor(for)) |
// each call
parent = seq and
i = numberOfVars and
child = SynthChild(MethodCallKind("each", false, 0))
or
exists(SimpleParameter param | param = TSimpleParameterSynth(block, 0) |
parent = param and
exists(MethodCall eachCall |
eachCall = TMethodCallSynth(seq, numberOfVars, "each", false, 0)
|
// receiver
parent = eachCall and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
child = childRef(for.getValue()) // value is the Enumerable
or
// assignment to pattern from for loop to synth parameter
parent = block and
parent = eachCall and
i = 1 and
child = SynthChild(AssignExprKind())
child = SynthChild(BraceBlockKind())
or
parent = TAssignExprSynth(block, 1) and
(
exists(Block block | block = TBraceBlockSynth(eachCall, 1) |
// block params
parent = block and
i = 0 and
child = childRef(for.getPattern())
child = SynthChild(SimpleParameterKind())
or
i = 1 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
exists(SimpleParameter param | param = TSimpleParameterSynth(block, 0) |
parent = param and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
or
// assignment to pattern from for loop to synth parameter
parent = block and
i = 1 and
child = SynthChild(AssignExprKind())
or
parent = TAssignExprSynth(block, 1) and
(
i = 0 and
child = childRef(for.getPattern())
or
i = 1 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
)
)
or
// rest of block body
parent = block and
child = childRef(for.getBody().(Do).getStmt(i - 2))
)
)
or
// rest of block body
parent = block and
child = childRef(for.getBody().(Do).getStmt(i - 2))
)
)
)
}
pragma[nomagic]
private predicate isDesugaredInitNode(ForExpr for, Variable v, AstNode n) {
exists(StmtSequence seq, AssignExpr ae |
seq = for.getDesugared() and
n = seq.getStmt(_) and
ae = n.(IfExpr).getThen() and
v = ae.getLeftOperand().getAVariable()
)
or
isDesugaredInitNode(for, v, n.getParent())
}
private class ForLoopSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
forLoopSynthesis(parent, i, child)
@@ -1338,6 +1443,14 @@ private module ForLoopDesugar {
final override predicate excludeFromControlFlowTree(AstNode n) {
n = any(ForExpr for).getBody()
}
final override predicate location(AstNode n, Location l) {
exists(ForExpr for, Ruby::AstNode access, Variable v |
forLoopVariableAccess(toTsFor(for), access, v) and
isDesugaredInitNode(for, v, n) and
l = access.getLocation()
)
}
}
}

View File

@@ -137,22 +137,6 @@ module LocalFlow {
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNodeImpl).getMethod())
}
/**
* Holds if `nodeFrom -> nodeTo` is a step from a parameter to a capture entry node for
* that parameter.
*
* This is intended to recover from flow not currently recognised by ordinary capture flow.
*/
predicate localFlowSsaParamCaptureInput(ParameterNodeImpl nodeFrom, Node nodeTo) {
exists(Ssa::CapturedEntryDefinition def |
nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def
|
nodeFrom.getParameter().(NamedParameter).getVariable() = def.getSourceVariable()
or
nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
)
}
/**
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
* involving SSA definition `def`.
@@ -285,6 +269,53 @@ predicate isNonConstantExpr(CfgNodes::ExprCfgNode n) {
not n.getExpr() instanceof ConstantAccess
}
/** Provides logic related to captured variables. */
module VariableCapture {
class CapturedVariable extends LocalVariable {
CapturedVariable() { this.isCaptured() }
CfgScope getCfgScope() {
exists(Scope scope | scope = this.getDeclaringScope() |
result = scope
or
result = scope.(ModuleBase).getCfgScope()
)
}
}
class CapturedSsaDefinitionExt extends SsaImpl::DefinitionExt {
CapturedSsaDefinitionExt() { this.getSourceVariable() instanceof CapturedVariable }
}
/**
* Holds if there is control-flow insensitive data-flow from `node1` to `node2`
* involving a captured variable. Only used in type tracking.
*/
predicate flowInsensitiveStep(Node node1, Node node2) {
exists(CapturedSsaDefinitionExt def, CapturedVariable v |
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node
def = node1.(SsaDefinitionExtNode).getDefinitionExt() and
def.getSourceVariable() = v and
(
def instanceof Ssa::WriteDefinition
or
def instanceof Ssa::SelfDefinition
) and
node2.(CapturedVariableNode).getVariable() = v
or
// From a captured variable node to its flow-sensitive capture nodes
node1.(CapturedVariableNode).getVariable() = v and
def = node2.(SsaDefinitionExtNode).getDefinitionExt() and
def.getSourceVariable() = v and
(
def instanceof Ssa::CapturedCallDefinition
or
def instanceof Ssa::CapturedEntryDefinition
)
)
}
}
/** A collection of cached types and predicates to be evaluated in the same stage. */
cached
private module Cached {
@@ -296,6 +327,7 @@ private module Cached {
TExprNode(CfgNodes::ExprCfgNode n) { TaintTrackingPrivate::forceCachingInSameStage() } or
TReturningNode(CfgNodes::ReturningCfgNode n) or
TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) or
TCapturedVariableNode(VariableCapture::CapturedVariable v) or
TNormalParameterNode(Parameter p) {
p instanceof SimpleParameter or
p instanceof OptionalParameter or
@@ -389,6 +421,8 @@ private module Cached {
LocalFlow::localFlowSsaInputFromRead(exprFrom, _, nodeTo) and
exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()]
)
or
VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo)
}
private predicate entrySsaDefinition(SsaDefinitionExtNode n) {
@@ -550,7 +584,7 @@ class SsaDefinitionExtNode extends NodeImpl, TSsaDefinitionExtNode {
}
/** An SSA definition for a `self` variable. */
class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionExtNode {
class SsaSelfDefinitionNode extends SsaDefinitionExtNode {
private SelfVariable self;
SsaSelfDefinitionNode() { self = def.getSourceVariable() }
@@ -559,6 +593,22 @@ class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionExtNode {
Scope getSelfScope() { result = self.getDeclaringScope() }
}
/** A data flow node representing a captured variable. Only used in type tracking. */
class CapturedVariableNode extends NodeImpl, TCapturedVariableNode {
private VariableCapture::CapturedVariable variable;
CapturedVariableNode() { this = TCapturedVariableNode(variable) }
/** Gets the captured variable represented by this node. */
VariableCapture::CapturedVariable getVariable() { result = variable }
override CfgScope getCfgScope() { result = variable.getCfgScope() }
override Location getLocationImpl() { result = variable.getLocation() }
override string toStringImpl() { result = "captured " + variable.getName() }
}
/**
* A value returning statement, viewed as a node in a data flow graph.
*
@@ -1163,13 +1213,7 @@ private module OutNodes {
import OutNodes
predicate jumpStep(Node pred, Node succ) {
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
succ.(SsaDefinitionExtNode).getDefinitionExt())
or
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
succ.(SsaDefinitionExtNode).getDefinitionExt())
or
predicate jumpStepTypeTracker(Node pred, Node succ) {
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
or
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(),
@@ -1178,6 +1222,16 @@ predicate jumpStep(Node pred, Node succ) {
any(AdditionalJumpStep s).step(pred, succ)
}
predicate jumpStep(Node pred, Node succ) {
jumpStepTypeTracker(pred, succ)
or
SsaImpl::captureFlowIn(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
succ.(SsaDefinitionExtNode).getDefinitionExt())
or
SsaImpl::captureFlowOut(_, pred.(SsaDefinitionExtNode).getDefinitionExt(),
succ.(SsaDefinitionExtNode).getDefinitionExt())
}
private ContentSet getKeywordContent(string name) {
exists(ConstantValue::ConstantSymbolValue key |
result.isSingleton(TKnownElementContent(key)) and

View File

@@ -363,10 +363,9 @@ private module Cached {
source = sink and
source instanceof LocalSourceNode
or
exists(Node mid | hasLocalSource(mid, source) |
exists(Node mid |
hasLocalSource(mid, source) and
localFlowStepTypeTracker(mid, sink)
or
LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
)
}

View File

@@ -74,7 +74,7 @@ predicate simpleLocalFlowStep = DataFlowPrivate::localFlowStepTypeTracker/2;
/**
* Holds if data can flow from `node1` to `node2` in a way that discards call contexts.
*/
predicate jumpStep = DataFlowPrivate::jumpStep/2;
predicate jumpStep = DataFlowPrivate::jumpStepTypeTracker/2;
/** Holds if there is direct flow from `param` to a return. */
pragma[nomagic]

View File

@@ -9,14 +9,45 @@
private import codeql.ruby.controlflow.internal.ControlFlowGraphImpl::TestOutput
private import codeql.IDEContextual
private import codeql.Locations
private import codeql.ruby.controlflow.ControlFlowGraph
/**
* Gets the source file to generate a CFG from.
*/
external string selectedSourceFile();
external string selectedSourceLine();
external string selectedSourceColumn();
bindingset[file, line, column]
private CfgScope smallestEnclosingScope(File file, int line, int column) {
result =
min(Location loc, CfgScope scope |
loc = scope.getLocation() and
(
loc.getStartLine() < line
or
loc.getStartLine() = line and loc.getStartColumn() <= column
) and
(
loc.getEndLine() > line
or
loc.getEndLine() = line and loc.getEndColumn() >= column
) and
loc.getFile() = file
|
scope
order by
loc.getStartLine() desc, loc.getStartColumn() desc, loc.getEndLine(), loc.getEndColumn()
)
}
class MyRelevantNode extends RelevantNode {
MyRelevantNode() {
this.getScope().getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile())
this.getScope() =
smallestEnclosingScope(getFileBySourceArchiveName(selectedSourceFile()),
selectedSourceLine().toInt(), selectedSourceColumn().toInt())
}
}

View File

@@ -24,29 +24,45 @@ calls/calls.rb:
# 67| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar
# 67| getReceiver: [ConstantReadAccess] X
# 226| [ForExpr] for ... in ...
# 226| getDesugared: [MethodCall] call to each
# 226| getReceiver: [MethodCall] call to bar
# 226| getReceiver: [SelfVariableAccess] self
# 226| getBlock: [BraceBlock] { ... }
# 226| getParameter: [SimpleParameter] __synth__0__1
# 226| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 226| getStmt: [AssignExpr] ... = ...
# 226| getDesugared: [StmtSequence] ...
# 226| getStmt: [IfExpr] if ...
# 226| getCondition: [NotExpr] ! ...
# 226| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 226| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
# 226| getBranch/getThen: [AssignExpr] ... = ...
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 227| getStmt: [MethodCall] call to baz
# 227| getReceiver: [SelfVariableAccess] self
# 226| getAnOperand/getRightOperand: [NilLiteral] nil
# 226| getStmt: [MethodCall] call to each
# 226| getReceiver: [MethodCall] call to bar
# 226| getReceiver: [SelfVariableAccess] self
# 226| getBlock: [BraceBlock] { ... }
# 226| getParameter: [SimpleParameter] __synth__0__1
# 226| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 226| getStmt: [AssignExpr] ... = ...
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 227| getStmt: [MethodCall] call to baz
# 227| getReceiver: [SelfVariableAccess] self
# 229| [ForExpr] for ... in ...
# 229| getDesugared: [MethodCall] call to each
# 229| getReceiver: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 229| getBlock: [BraceBlock] { ... }
# 229| getParameter: [SimpleParameter] __synth__0__1
# 229| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 229| getStmt: [AssignExpr] ... = ...
# 229| getDesugared: [StmtSequence] ...
# 229| getStmt: [IfExpr] if ...
# 229| getCondition: [NotExpr] ! ...
# 229| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 229| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
# 229| getBranch/getThen: [AssignExpr] ... = ...
# 229| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 230| getStmt: [MethodCall] call to baz
# 230| getReceiver: [ConstantReadAccess] X
# 229| getAnOperand/getRightOperand: [NilLiteral] nil
# 229| getStmt: [MethodCall] call to each
# 229| getReceiver: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 229| getBlock: [BraceBlock] { ... }
# 229| getParameter: [SimpleParameter] __synth__0__1
# 229| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 229| getStmt: [AssignExpr] ... = ...
# 229| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 230| getStmt: [MethodCall] call to baz
# 230| getReceiver: [ConstantReadAccess] X
# 249| [HashLiteral] {...}
# 249| getDesugared: [MethodCall] call to []
# 249| getReceiver: [ConstantReadAccess] Hash
@@ -245,52 +261,74 @@ calls/calls.rb:
# 322| getArgument: [LocalVariableAccess] __synth__4
# 322| getStmt: [LocalVariableAccess] __synth__4
# 342| [ForExpr] for ... in ...
# 342| getDesugared: [MethodCall] call to each
# 342| getReceiver: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 1
# 342| getArgument: [IntegerLiteral] 2
# 342| getArgument: [IntegerLiteral] 3
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 4
# 342| getArgument: [IntegerLiteral] 5
# 342| getArgument: [IntegerLiteral] 6
# 342| getBlock: [BraceBlock] { ... }
# 342| getParameter: [SimpleParameter] __synth__0__1
# 342| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 342| getStmt: [AssignExpr] ... = ...
# 342| getDesugared: [StmtSequence] ...
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3__1
# 342| getAnOperand/getRightOperand: [SplatExpr] * ...
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
# 342| getArgument: [IntegerLiteral] 0
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] y
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
# 342| getDesugared: [StmtSequence] ...
# 342| getStmt: [IfExpr] if ...
# 342| getCondition: [NotExpr] ! ...
# 342| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
# 342| getBranch/getThen: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 342| getAnOperand/getRightOperand: [NilLiteral] nil
# 342| getStmt: [IfExpr] if ...
# 342| getCondition: [NotExpr] ! ...
# 342| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] y
# 342| getBranch/getThen: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] y
# 342| getAnOperand/getRightOperand: [NilLiteral] nil
# 342| getStmt: [IfExpr] if ...
# 342| getCondition: [NotExpr] ! ...
# 342| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] z
# 342| getBranch/getThen: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] z
# 342| getAnOperand/getRightOperand: [NilLiteral] nil
# 342| getStmt: [MethodCall] call to each
# 342| getReceiver: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 1
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] z
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
# 342| getArgument: [IntegerLiteral] 2
# 342| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
# 343| getStmt: [MethodCall] call to foo
# 343| getReceiver: [SelfVariableAccess] self
# 343| getArgument: [LocalVariableAccess] x
# 343| getArgument: [LocalVariableAccess] y
# 343| getArgument: [LocalVariableAccess] z
# 342| getArgument: [IntegerLiteral] 3
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 4
# 342| getArgument: [IntegerLiteral] 5
# 342| getArgument: [IntegerLiteral] 6
# 342| getBlock: [BraceBlock] { ... }
# 342| getParameter: [SimpleParameter] __synth__0__1
# 342| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 342| getStmt: [AssignExpr] ... = ...
# 342| getDesugared: [StmtSequence] ...
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3__1
# 342| getAnOperand/getRightOperand: [SplatExpr] * ...
# 342| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
# 342| getArgument: [IntegerLiteral] 0
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] y
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
# 342| getArgument: [IntegerLiteral] 1
# 342| getStmt: [AssignExpr] ... = ...
# 342| getAnOperand/getLeftOperand: [LocalVariableAccess] z
# 342| getAnOperand/getRightOperand: [MethodCall] call to []
# 342| getReceiver: [LocalVariableAccess] __synth__3__1
# 342| getArgument: [IntegerLiteral] 2
# 342| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
# 343| getStmt: [MethodCall] call to foo
# 343| getReceiver: [SelfVariableAccess] self
# 343| getArgument: [LocalVariableAccess] x
# 343| getArgument: [LocalVariableAccess] y
# 343| getArgument: [LocalVariableAccess] z
# 364| [MethodCall] call to empty?
# 364| getDesugared: [StmtSequence] ...
# 364| getStmt: [AssignExpr] ... = ...
@@ -594,139 +632,185 @@ literals/literals.rb:
# 199| getValue: [ConstantReadAccess] Z
control/loops.rb:
# 9| [ForExpr] for ... in ...
# 9| getDesugared: [MethodCall] call to each
# 9| getReceiver: [RangeLiteral] _ .. _
# 9| getBegin: [IntegerLiteral] 1
# 9| getEnd: [IntegerLiteral] 10
# 9| getBlock: [BraceBlock] { ... }
# 9| getParameter: [SimpleParameter] __synth__0__1
# 9| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 9| getStmt: [AssignExpr] ... = ...
# 9| getDesugared: [StmtSequence] ...
# 9| getStmt: [IfExpr] if ...
# 9| getCondition: [NotExpr] ! ...
# 9| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 9| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] n
# 9| getBranch/getThen: [AssignExpr] ... = ...
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 9| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 10| getStmt: [AssignAddExpr] ... += ...
# 10| getDesugared: [AssignExpr] ... = ...
# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 10| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 10| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 10| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 11| getStmt: [AssignExpr] ... = ...
# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n
# 9| getAnOperand/getRightOperand: [NilLiteral] nil
# 9| getStmt: [MethodCall] call to each
# 9| getReceiver: [RangeLiteral] _ .. _
# 9| getBegin: [IntegerLiteral] 1
# 9| getEnd: [IntegerLiteral] 10
# 9| getBlock: [BraceBlock] { ... }
# 9| getParameter: [SimpleParameter] __synth__0__1
# 9| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 9| getStmt: [AssignExpr] ... = ...
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 9| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 10| getStmt: [AssignAddExpr] ... += ...
# 10| getDesugared: [AssignExpr] ... = ...
# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 10| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 10| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 10| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 11| getStmt: [AssignExpr] ... = ...
# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n
# 16| [ForExpr] for ... in ...
# 16| getDesugared: [MethodCall] call to each
# 16| getReceiver: [RangeLiteral] _ .. _
# 16| getBegin: [IntegerLiteral] 1
# 16| getEnd: [IntegerLiteral] 10
# 16| getBlock: [BraceBlock] { ... }
# 16| getParameter: [SimpleParameter] __synth__0__1
# 16| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 16| getStmt: [AssignExpr] ... = ...
# 16| getDesugared: [StmtSequence] ...
# 16| getStmt: [IfExpr] if ...
# 16| getCondition: [NotExpr] ! ...
# 16| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 16| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] n
# 16| getBranch/getThen: [AssignExpr] ... = ...
# 16| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 16| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 17| getStmt: [AssignAddExpr] ... += ...
# 17| getDesugared: [AssignExpr] ... = ...
# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 17| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 17| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 18| getStmt: [AssignSubExpr] ... -= ...
# 18| getDesugared: [AssignExpr] ... = ...
# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 18| getAnOperand/getRightOperand: [SubExpr] ... - ...
# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 16| getAnOperand/getRightOperand: [NilLiteral] nil
# 16| getStmt: [MethodCall] call to each
# 16| getReceiver: [RangeLiteral] _ .. _
# 16| getBegin: [IntegerLiteral] 1
# 16| getEnd: [IntegerLiteral] 10
# 16| getBlock: [BraceBlock] { ... }
# 16| getParameter: [SimpleParameter] __synth__0__1
# 16| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 16| getStmt: [AssignExpr] ... = ...
# 16| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 16| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 17| getStmt: [AssignAddExpr] ... += ...
# 17| getDesugared: [AssignExpr] ... = ...
# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 17| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 17| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 17| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 18| getStmt: [AssignSubExpr] ... -= ...
# 18| getDesugared: [AssignExpr] ... = ...
# 18| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 18| getAnOperand/getRightOperand: [SubExpr] ... - ...
# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 22| [ForExpr] for ... in ...
# 22| getDesugared: [MethodCall] call to each
# 22| getReceiver: [HashLiteral] {...}
# 22| getDesugared: [MethodCall] call to []
# 22| getReceiver: [ConstantReadAccess] Hash
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
# 22| getComponent: [StringTextComponent] foo
# 22| getValue: [IntegerLiteral] 0
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :bar
# 22| getComponent: [StringTextComponent] bar
# 22| getValue: [IntegerLiteral] 1
# 22| getBlock: [BraceBlock] { ... }
# 22| getParameter: [SimpleParameter] __synth__0__1
# 22| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 22| getStmt: [AssignExpr] ... = ...
# 22| getDesugared: [StmtSequence] ...
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
# 22| getAnOperand/getRightOperand: [SplatExpr] * ...
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
# 22| getArgument: [IntegerLiteral] 0
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
# 22| getArgument: [IntegerLiteral] 1
# 22| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
# 23| getStmt: [AssignAddExpr] ... += ...
# 23| getDesugared: [AssignExpr] ... = ...
# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 23| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 23| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 23| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 24| getStmt: [AssignMulExpr] ... *= ...
# 24| getDesugared: [AssignExpr] ... = ...
# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 24| getAnOperand/getRightOperand: [MulExpr] ... * ...
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 22| getDesugared: [StmtSequence] ...
# 22| getStmt: [IfExpr] if ...
# 22| getCondition: [NotExpr] ! ...
# 22| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] key
# 22| getBranch/getThen: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 22| getAnOperand/getRightOperand: [NilLiteral] nil
# 22| getStmt: [IfExpr] if ...
# 22| getCondition: [NotExpr] ! ...
# 22| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] value
# 22| getBranch/getThen: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 22| getAnOperand/getRightOperand: [NilLiteral] nil
# 22| getStmt: [MethodCall] call to each
# 22| getReceiver: [HashLiteral] {...}
# 22| getDesugared: [MethodCall] call to []
# 22| getReceiver: [ConstantReadAccess] Hash
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
# 22| getComponent: [StringTextComponent] foo
# 22| getValue: [IntegerLiteral] 0
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :bar
# 22| getComponent: [StringTextComponent] bar
# 22| getValue: [IntegerLiteral] 1
# 22| getBlock: [BraceBlock] { ... }
# 22| getParameter: [SimpleParameter] __synth__0__1
# 22| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 22| getStmt: [AssignExpr] ... = ...
# 22| getDesugared: [StmtSequence] ...
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
# 22| getAnOperand/getRightOperand: [SplatExpr] * ...
# 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
# 22| getArgument: [IntegerLiteral] 0
# 22| getStmt: [AssignExpr] ... = ...
# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 22| getAnOperand/getRightOperand: [MethodCall] call to []
# 22| getReceiver: [LocalVariableAccess] __synth__2__1
# 22| getArgument: [IntegerLiteral] 1
# 22| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
# 23| getStmt: [AssignAddExpr] ... += ...
# 23| getDesugared: [AssignExpr] ... = ...
# 23| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 23| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 23| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 23| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 24| getStmt: [AssignMulExpr] ... *= ...
# 24| getDesugared: [AssignExpr] ... = ...
# 24| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 24| getAnOperand/getRightOperand: [MulExpr] ... * ...
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 28| [ForExpr] for ... in ...
# 28| getDesugared: [MethodCall] call to each
# 28| getReceiver: [HashLiteral] {...}
# 28| getDesugared: [MethodCall] call to []
# 28| getReceiver: [ConstantReadAccess] Hash
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
# 28| getComponent: [StringTextComponent] foo
# 28| getValue: [IntegerLiteral] 0
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :bar
# 28| getComponent: [StringTextComponent] bar
# 28| getValue: [IntegerLiteral] 1
# 28| getBlock: [BraceBlock] { ... }
# 28| getParameter: [SimpleParameter] __synth__0__1
# 28| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignExpr] ... = ...
# 28| getDesugared: [StmtSequence] ...
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
# 28| getAnOperand/getRightOperand: [SplatExpr] * ...
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
# 28| getArgument: [IntegerLiteral] 0
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
# 28| getArgument: [IntegerLiteral] 1
# 28| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
# 29| getStmt: [AssignAddExpr] ... += ...
# 29| getDesugared: [AssignExpr] ... = ...
# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 29| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 29| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 29| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 30| getStmt: [AssignDivExpr] ... /= ...
# 30| getDesugared: [AssignExpr] ... = ...
# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 30| getAnOperand/getRightOperand: [DivExpr] ... / ...
# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 31| getStmt: [BreakStmt] break
# 28| getDesugared: [StmtSequence] ...
# 28| getStmt: [IfExpr] if ...
# 28| getCondition: [NotExpr] ! ...
# 28| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] key
# 28| getBranch/getThen: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 28| getAnOperand/getRightOperand: [NilLiteral] nil
# 28| getStmt: [IfExpr] if ...
# 28| getCondition: [NotExpr] ! ...
# 28| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] value
# 28| getBranch/getThen: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 28| getAnOperand/getRightOperand: [NilLiteral] nil
# 28| getStmt: [MethodCall] call to each
# 28| getReceiver: [HashLiteral] {...}
# 28| getDesugared: [MethodCall] call to []
# 28| getReceiver: [ConstantReadAccess] Hash
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
# 28| getComponent: [StringTextComponent] foo
# 28| getValue: [IntegerLiteral] 0
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :bar
# 28| getComponent: [StringTextComponent] bar
# 28| getValue: [IntegerLiteral] 1
# 28| getBlock: [BraceBlock] { ... }
# 28| getParameter: [SimpleParameter] __synth__0__1
# 28| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignExpr] ... = ...
# 28| getDesugared: [StmtSequence] ...
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2__1
# 28| getAnOperand/getRightOperand: [SplatExpr] * ...
# 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
# 28| getArgument: [IntegerLiteral] 0
# 28| getStmt: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value
# 28| getAnOperand/getRightOperand: [MethodCall] call to []
# 28| getReceiver: [LocalVariableAccess] __synth__2__1
# 28| getArgument: [IntegerLiteral] 1
# 28| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
# 29| getStmt: [AssignAddExpr] ... += ...
# 29| getDesugared: [AssignExpr] ... = ...
# 29| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
# 29| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 29| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] sum
# 29| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 30| getStmt: [AssignDivExpr] ... /= ...
# 30| getDesugared: [AssignExpr] ... = ...
# 30| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 30| getAnOperand/getRightOperand: [DivExpr] ... / ...
# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 31| getStmt: [BreakStmt] break
# 36| [AssignAddExpr] ... += ...
# 36| getDesugared: [AssignExpr] ... = ...
# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x
@@ -985,29 +1069,37 @@ params/params.rb:
# 21| getReceiver: [ConstantReadAccess] Array
erb/template.html.erb:
# 27| [ForExpr] for ... in ...
# 27| getDesugared: [MethodCall] call to each
# 27| getReceiver: [ArrayLiteral] [...]
# 27| getDesugared: [MethodCall] call to []
# 27| getReceiver: [ConstantReadAccess] Array
# 27| getArgument: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo
# 27| getArgument: [StringLiteral] "bar"
# 27| getComponent: [StringTextComponent] bar
# 27| getArgument: [StringLiteral] "baz"
# 27| getComponent: [StringTextComponent] baz
# 27| getBlock: [BraceBlock] { ... }
# 27| getParameter: [SimpleParameter] __synth__0__1
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 27| getStmt: [AssignExpr] ... = ...
# 27| getDesugared: [StmtSequence] ...
# 27| getStmt: [IfExpr] if ...
# 27| getCondition: [NotExpr] ! ...
# 27| getAnOperand/getOperand/getReceiver: [DefinedExpr] defined? ...
# 27| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] x
# 27| getBranch/getThen: [AssignExpr] ... = ...
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 29| getStmt: [LocalVariableAccess] xs
# 27| getAnOperand/getRightOperand: [NilLiteral] nil
# 27| getStmt: [MethodCall] call to each
# 27| getReceiver: [ArrayLiteral] [...]
# 27| getDesugared: [MethodCall] call to []
# 27| getReceiver: [ConstantReadAccess] Array
# 27| getArgument: [StringLiteral] "foo"
# 27| getComponent: [StringTextComponent] foo
# 27| getArgument: [StringLiteral] "bar"
# 27| getComponent: [StringTextComponent] bar
# 27| getArgument: [StringLiteral] "baz"
# 27| getComponent: [StringTextComponent] baz
# 27| getBlock: [BraceBlock] { ... }
# 27| getParameter: [SimpleParameter] __synth__0__1
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 27| getStmt: [AssignExpr] ... = ...
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 29| getStmt: [LocalVariableAccess] xs
gems/test.gemspec:
# 2| [AssignExpr] ... = ...
# 2| getDesugared: [StmtSequence] ...

View File

@@ -10,6 +10,8 @@ exprValue
| calls/calls.rb:26:7:26:7 | 1 | 1 | int |
| calls/calls.rb:36:9:36:11 | 100 | 100 | int |
| calls/calls.rb:36:14:36:16 | 200 | 200 | int |
| calls/calls.rb:226:5:226:5 | nil | nil | nil |
| calls/calls.rb:229:5:229:5 | nil | nil | nil |
| calls/calls.rb:280:5:280:8 | :blah | :blah | symbol |
| calls/calls.rb:281:5:281:8 | :blah | :blah | symbol |
| calls/calls.rb:290:11:290:16 | "blah" | blah | string |
@@ -58,8 +60,11 @@ exprValue
| calls/calls.rb:322:37:322:37 | 2 | 2 | int |
| calls/calls.rb:330:31:330:37 | "error" | error | string |
| calls/calls.rb:342:5:342:5 | 0 | 0 | int |
| calls/calls.rb:342:5:342:5 | nil | nil | nil |
| calls/calls.rb:342:8:342:8 | 1 | 1 | int |
| calls/calls.rb:342:8:342:8 | nil | nil | nil |
| calls/calls.rb:342:11:342:11 | 2 | 2 | int |
| calls/calls.rb:342:11:342:11 | nil | nil | nil |
| calls/calls.rb:342:18:342:18 | 1 | 1 | int |
| calls/calls.rb:342:20:342:20 | 2 | 2 | int |
| calls/calls.rb:342:22:342:22 | 3 | 3 | int |
@@ -330,18 +335,24 @@ exprValue
| control/loops.rb:4:5:4:5 | 0 | 0 | int |
| control/loops.rb:5:5:5:5 | 0 | 0 | int |
| control/loops.rb:6:5:6:5 | 0 | 0 | int |
| control/loops.rb:9:5:9:5 | nil | nil | nil |
| control/loops.rb:9:10:9:10 | 1 | 1 | int |
| control/loops.rb:9:13:9:14 | 10 | 10 | int |
| control/loops.rb:16:5:16:5 | nil | nil | nil |
| control/loops.rb:16:10:16:10 | 1 | 1 | int |
| control/loops.rb:16:13:16:14 | 10 | 10 | int |
| control/loops.rb:22:5:22:7 | 0 | 0 | int |
| control/loops.rb:22:5:22:7 | nil | nil | nil |
| control/loops.rb:22:10:22:14 | 1 | 1 | int |
| control/loops.rb:22:10:22:14 | nil | nil | nil |
| control/loops.rb:22:20:22:22 | :foo | :foo | symbol |
| control/loops.rb:22:25:22:25 | 0 | 0 | int |
| control/loops.rb:22:28:22:30 | :bar | :bar | symbol |
| control/loops.rb:22:33:22:33 | 1 | 1 | int |
| control/loops.rb:28:6:28:8 | 0 | 0 | int |
| control/loops.rb:28:6:28:8 | nil | nil | nil |
| control/loops.rb:28:11:28:15 | 1 | 1 | int |
| control/loops.rb:28:11:28:15 | nil | nil | nil |
| control/loops.rb:28:22:28:24 | :foo | :foo | symbol |
| control/loops.rb:28:27:28:27 | 0 | 0 | int |
| control/loops.rb:28:30:28:32 | :bar | :bar | symbol |
@@ -365,6 +376,7 @@ exprValue
| control/loops.rb:66:11:66:11 | y | 0 | int |
| erb/template.html.erb:19:5:19:17 | "hello world" | hello world | string |
| erb/template.html.erb:25:9:25:10 | "" | | string |
| erb/template.html.erb:27:10:27:10 | nil | nil | nil |
| erb/template.html.erb:27:16:27:20 | "foo" | foo | string |
| erb/template.html.erb:27:23:27:27 | "bar" | bar | string |
| erb/template.html.erb:27:30:27:34 | "baz" | baz | string |
@@ -934,6 +946,8 @@ exprCfgNodeValue
| calls/calls.rb:26:7:26:7 | 1 | 1 | int |
| calls/calls.rb:36:9:36:11 | 100 | 100 | int |
| calls/calls.rb:36:14:36:16 | 200 | 200 | int |
| calls/calls.rb:226:5:226:5 | nil | nil | nil |
| calls/calls.rb:229:5:229:5 | nil | nil | nil |
| calls/calls.rb:280:5:280:8 | :blah | :blah | symbol |
| calls/calls.rb:281:5:281:8 | :blah | :blah | symbol |
| calls/calls.rb:290:11:290:16 | "blah" | blah | string |
@@ -982,8 +996,11 @@ exprCfgNodeValue
| calls/calls.rb:322:37:322:37 | 2 | 2 | int |
| calls/calls.rb:330:31:330:37 | "error" | error | string |
| calls/calls.rb:342:5:342:5 | 0 | 0 | int |
| calls/calls.rb:342:5:342:5 | nil | nil | nil |
| calls/calls.rb:342:8:342:8 | 1 | 1 | int |
| calls/calls.rb:342:8:342:8 | nil | nil | nil |
| calls/calls.rb:342:11:342:11 | 2 | 2 | int |
| calls/calls.rb:342:11:342:11 | nil | nil | nil |
| calls/calls.rb:342:18:342:18 | 1 | 1 | int |
| calls/calls.rb:342:20:342:20 | 2 | 2 | int |
| calls/calls.rb:342:22:342:22 | 3 | 3 | int |
@@ -1226,18 +1243,24 @@ exprCfgNodeValue
| control/loops.rb:4:5:4:5 | 0 | 0 | int |
| control/loops.rb:5:5:5:5 | 0 | 0 | int |
| control/loops.rb:6:5:6:5 | 0 | 0 | int |
| control/loops.rb:9:5:9:5 | nil | nil | nil |
| control/loops.rb:9:10:9:10 | 1 | 1 | int |
| control/loops.rb:9:13:9:14 | 10 | 10 | int |
| control/loops.rb:16:5:16:5 | nil | nil | nil |
| control/loops.rb:16:10:16:10 | 1 | 1 | int |
| control/loops.rb:16:13:16:14 | 10 | 10 | int |
| control/loops.rb:22:5:22:7 | 0 | 0 | int |
| control/loops.rb:22:5:22:7 | nil | nil | nil |
| control/loops.rb:22:10:22:14 | 1 | 1 | int |
| control/loops.rb:22:10:22:14 | nil | nil | nil |
| control/loops.rb:22:20:22:22 | :foo | :foo | symbol |
| control/loops.rb:22:25:22:25 | 0 | 0 | int |
| control/loops.rb:22:28:22:30 | :bar | :bar | symbol |
| control/loops.rb:22:33:22:33 | 1 | 1 | int |
| control/loops.rb:28:6:28:8 | 0 | 0 | int |
| control/loops.rb:28:6:28:8 | nil | nil | nil |
| control/loops.rb:28:11:28:15 | 1 | 1 | int |
| control/loops.rb:28:11:28:15 | nil | nil | nil |
| control/loops.rb:28:22:28:24 | :foo | :foo | symbol |
| control/loops.rb:28:27:28:27 | 0 | 0 | int |
| control/loops.rb:28:30:28:32 | :bar | :bar | symbol |
@@ -1261,6 +1284,7 @@ exprCfgNodeValue
| control/loops.rb:66:11:66:11 | y | 0 | int |
| erb/template.html.erb:19:5:19:17 | "hello world" | hello world | string |
| erb/template.html.erb:25:9:25:10 | "" | | string |
| erb/template.html.erb:27:10:27:10 | nil | nil | nil |
| erb/template.html.erb:27:16:27:20 | "foo" | foo | string |
| erb/template.html.erb:27:23:27:27 | "bar" | bar | string |
| erb/template.html.erb:27:30:27:34 | "baz" | baz | string |

View File

@@ -251,9 +251,13 @@ callsWithReceiver
| calls.rb:223:1:223:6 | call to bar | calls.rb:223:1:223:1 | X |
| calls.rb:223:14:223:19 | call to foo | calls.rb:223:14:223:14 | X |
| calls.rb:226:1:228:3 | call to each | calls.rb:226:10:226:12 | call to bar |
| calls.rb:226:5:226:5 | ! ... | calls.rb:226:5:226:5 | defined? ... |
| calls.rb:226:5:226:5 | defined? ... | calls.rb:226:5:226:5 | x |
| calls.rb:226:10:226:12 | call to bar | calls.rb:226:10:226:12 | self |
| calls.rb:227:3:227:5 | call to baz | calls.rb:227:3:227:5 | self |
| calls.rb:229:1:231:3 | call to each | calls.rb:229:10:229:15 | call to bar |
| calls.rb:229:5:229:5 | ! ... | calls.rb:229:5:229:5 | defined? ... |
| calls.rb:229:5:229:5 | defined? ... | calls.rb:229:5:229:5 | x |
| calls.rb:229:10:229:15 | call to bar | calls.rb:229:10:229:10 | X |
| calls.rb:230:3:230:8 | call to baz | calls.rb:230:3:230:3 | X |
| calls.rb:234:1:234:3 | call to foo | calls.rb:234:1:234:3 | self |
@@ -376,9 +380,15 @@ callsWithReceiver
| calls.rb:338:3:338:13 | call to bar | calls.rb:338:3:338:13 | self |
| calls.rb:342:1:344:3 | * ... | calls.rb:342:1:344:3 | __synth__0__1 |
| calls.rb:342:1:344:3 | call to each | calls.rb:342:16:342:33 | [...] |
| calls.rb:342:5:342:5 | ! ... | calls.rb:342:5:342:5 | defined? ... |
| calls.rb:342:5:342:5 | call to [] | calls.rb:342:5:342:5 | __synth__3__1 |
| calls.rb:342:5:342:5 | defined? ... | calls.rb:342:5:342:5 | x |
| calls.rb:342:8:342:8 | ! ... | calls.rb:342:8:342:8 | defined? ... |
| calls.rb:342:8:342:8 | call to [] | calls.rb:342:8:342:8 | __synth__3__1 |
| calls.rb:342:8:342:8 | defined? ... | calls.rb:342:8:342:8 | y |
| calls.rb:342:11:342:11 | ! ... | calls.rb:342:11:342:11 | defined? ... |
| calls.rb:342:11:342:11 | call to [] | calls.rb:342:11:342:11 | __synth__3__1 |
| calls.rb:342:11:342:11 | defined? ... | calls.rb:342:11:342:11 | z |
| calls.rb:342:16:342:33 | call to [] | calls.rb:342:16:342:33 | Array |
| calls.rb:342:17:342:23 | call to [] | calls.rb:342:17:342:23 | Array |
| calls.rb:342:26:342:32 | call to [] | calls.rb:342:26:342:32 | Array |

View File

@@ -22,6 +22,12 @@ conditionalExprs
| conditionals.rb:61:1:64:3 | if ... | IfExpr | conditionals.rb:61:4:61:8 | ... > ... | conditionals.rb:63:1:63:4 | else ... | false |
| conditionals.rb:67:1:70:3 | if ... | IfExpr | conditionals.rb:67:4:67:8 | ... > ... | conditionals.rb:67:10:67:13 | then ... | true |
| conditionals.rb:67:1:70:3 | if ... | IfExpr | conditionals.rb:67:4:67:8 | ... > ... | conditionals.rb:68:1:69:5 | else ... | false |
| loops.rb:9:5:9:5 | if ... | IfExpr | loops.rb:9:5:9:5 | ! ... | loops.rb:9:5:9:5 | ... = ... | true |
| loops.rb:16:5:16:5 | if ... | IfExpr | loops.rb:16:5:16:5 | ! ... | loops.rb:16:5:16:5 | ... = ... | true |
| loops.rb:22:5:22:7 | if ... | IfExpr | loops.rb:22:5:22:7 | ! ... | loops.rb:22:5:22:7 | ... = ... | true |
| loops.rb:22:10:22:14 | if ... | IfExpr | loops.rb:22:10:22:14 | ! ... | loops.rb:22:10:22:14 | ... = ... | true |
| loops.rb:28:6:28:8 | if ... | IfExpr | loops.rb:28:6:28:8 | ! ... | loops.rb:28:6:28:8 | ... = ... | true |
| loops.rb:28:11:28:15 | if ... | IfExpr | loops.rb:28:11:28:15 | ! ... | loops.rb:28:11:28:15 | ... = ... | true |
ifExprs
| conditionals.rb:10:1:12:3 | if ... | IfExpr | conditionals.rb:10:4:10:8 | ... > ... | conditionals.rb:10:10:11:5 | then ... | (none) | false |
| conditionals.rb:15:1:19:3 | if ... | IfExpr | conditionals.rb:15:4:15:9 | ... == ... | conditionals.rb:15:10:16:5 | then ... | else ... | false |

View File

@@ -27,9 +27,15 @@
| conditionals.rb:61:1:64:3 | if ... | IfExpr |
| conditionals.rb:67:1:70:3 | if ... | IfExpr |
| loops.rb:9:1:12:3 | for ... in ... | ForExpr |
| loops.rb:9:5:9:5 | if ... | IfExpr |
| loops.rb:16:1:19:3 | for ... in ... | ForExpr |
| loops.rb:16:5:16:5 | if ... | IfExpr |
| loops.rb:22:1:25:3 | for ... in ... | ForExpr |
| loops.rb:22:5:22:7 | if ... | IfExpr |
| loops.rb:22:10:22:14 | if ... | IfExpr |
| loops.rb:28:1:32:3 | for ... in ... | ForExpr |
| loops.rb:28:6:28:8 | if ... | IfExpr |
| loops.rb:28:11:28:15 | if ... | IfExpr |
| loops.rb:35:1:39:3 | while ... | WhileExpr |
| loops.rb:42:1:45:3 | while ... | WhileExpr |
| loops.rb:48:1:48:19 | ... while ... | WhileModifierExpr |

View File

@@ -2377,7 +2377,7 @@ cfg.rb:
#-----| -> \u1234
# 88| ... = ...
#-----| -> Array
#-----| -> x
# 88| "\u1234#{...}\n"
#-----| -> ... = ...
@@ -2394,9 +2394,21 @@ cfg.rb:
# 88| \n
#-----| -> "\u1234#{...}\n"
# 90| ...
#-----| -> $global
# 90| ... = ...
#-----| -> if ...
# 90| ... = ...
#-----| -> x
# 90| [false] ! ...
#-----| false -> if ...
# 90| [true] ! ...
#-----| true -> x
# 90| __synth__0__1
#-----| -> x
@@ -2404,7 +2416,11 @@ cfg.rb:
#-----| -> ... = ...
# 90| call to each
#-----| -> $global
#-----| -> ...
# 90| defined? ...
#-----| false -> [true] ! ...
#-----| true -> [false] ! ...
# 90| enter { ... }
#-----| -> __synth__0__1
@@ -2414,6 +2430,18 @@ cfg.rb:
# 90| exit { ... } (normal)
#-----| -> exit { ... }
# 90| if ...
#-----| -> Array
# 90| nil
#-----| -> ... = ...
# 90| x
#-----| -> nil
# 90| x
#-----| -> defined? ...
# 90| { ... }
#-----| -> call to each

View File

@@ -26,6 +26,9 @@ callsWithNoArguments
| cfg.rb:62:7:62:12 | * ... |
| cfg.rb:62:17:62:27 | * ... |
| cfg.rb:90:1:93:3 | call to each |
| cfg.rb:90:5:90:5 | [false] ! ... |
| cfg.rb:90:5:90:5 | [true] ! ... |
| cfg.rb:90:5:90:5 | defined? ... |
| cfg.rb:98:10:98:15 | ** ... |
| cfg.rb:98:30:98:35 | ** ... |
| cfg.rb:138:17:138:23 | * ... |

View File

@@ -15,7 +15,6 @@ testFailures
| array_flow.rb:376:10:376:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
| array_flow.rb:377:10:377:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
| array_flow.rb:378:10:378:13 | ...[...] | Unexpected result: hasValueFlow=42.3 |
| array_flow.rb:407:12:407:30 | # $ hasValueFlow=45 | Missing result:hasValueFlow=45 |
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.4 |
| array_flow.rb:484:10:484:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |

View File

@@ -2399,6 +2399,7 @@
| UseUseExplosion.rb:24:5:25:7 | use | UseUseExplosion.rb:1:1:26:3 | C |
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) |
| local_dataflow.rb:1:1:150:3 | <uninitialized> | local_dataflow.rb:10:9:10:9 | x |
| local_dataflow.rb:1:1:150:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a |
@@ -2424,21 +2425,33 @@
| local_dataflow.rb:9:1:9:5 | array | local_dataflow.rb:10:14:10:18 | array |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:5 | array |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:10:5:13:3 | ... | local_dataflow.rb:10:1:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | <captured entry> self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | <captured exit> x | local_dataflow.rb:15:5:15:5 | x |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:9:10:9 | x |
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:1:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:5:13:3 | ... |
| local_dataflow.rb:10:9:10:9 | ... = ... | local_dataflow.rb:10:9:10:9 | if ... |
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | ... = ... |
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | x |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | phi |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:12:5:12:5 | x |
| local_dataflow.rb:10:14:10:18 | [post] array | local_dataflow.rb:15:10:15:14 | array |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:15:10:15:14 | array |
| local_dataflow.rb:11:1:11:2 | [post] self | local_dataflow.rb:12:3:12:5 | self |
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:12:3:12:5 | self |
| local_dataflow.rb:15:1:17:3 | <captured exit> x | local_dataflow.rb:19:5:19:5 | x |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | ... = ... |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:5:15:5 | x |
| local_dataflow.rb:15:1:17:3 | call to each | local_dataflow.rb:15:1:17:3 | ... |
| local_dataflow.rb:15:5:15:5 | ... = ... | local_dataflow.rb:15:5:15:5 | if ... |
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | ... = ... |
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | x |
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | phi |
| local_dataflow.rb:15:10:15:14 | [post] array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:16:9:16:10 | 10 | local_dataflow.rb:16:3:16:10 | break |
@@ -2446,6 +2459,11 @@
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:5:19:5 | x |
| local_dataflow.rb:19:1:21:3 | call to each | local_dataflow.rb:19:1:21:3 | ... |
| local_dataflow.rb:19:5:19:5 | ... = ... | local_dataflow.rb:19:5:19:5 | if ... |
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | ... = ... |
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | x |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | phi |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:20:6:20:6 | x |
| local_dataflow.rb:24:2:24:8 | break | local_dataflow.rb:23:1:25:3 | while ... |
| local_dataflow.rb:24:8:24:8 | 5 | local_dataflow.rb:24:2:24:8 | break |

View File

@@ -832,13 +832,22 @@ arg
| local_dataflow.rb:9:12:9:12 | 2 | local_dataflow.rb:9:9:9:15 | call to [] | position 1 |
| local_dataflow.rb:9:14:9:14 | 3 | local_dataflow.rb:9:9:9:15 | call to [] | position 2 |
| local_dataflow.rb:10:5:13:3 | { ... } | local_dataflow.rb:10:5:13:3 | call to each | block |
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [false] ! ... | self |
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [true] ! ... | self |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | defined? ... | self |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:10:5:13:3 | call to each | self |
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:11:1:11:2 | call to do | self |
| local_dataflow.rb:12:3:12:5 | self | local_dataflow.rb:12:3:12:5 | call to p | self |
| local_dataflow.rb:12:5:12:5 | x | local_dataflow.rb:12:3:12:5 | call to p | position 0 |
| local_dataflow.rb:15:1:17:3 | { ... } | local_dataflow.rb:15:1:17:3 | call to each | block |
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [false] ! ... | self |
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [true] ! ... | self |
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | defined? ... | self |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:15:1:17:3 | call to each | self |
| local_dataflow.rb:19:1:21:3 | { ... } | local_dataflow.rb:19:1:21:3 | call to each | block |
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [false] ! ... | self |
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [true] ! ... | self |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | defined? ... | self |
| local_dataflow.rb:19:10:19:14 | array | local_dataflow.rb:19:1:21:3 | call to each | self |
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... | self |
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... | position 0 |

View File

@@ -2842,6 +2842,7 @@
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) |
| local_dataflow.rb:1:1:7:3 | synthetic *args | local_dataflow.rb:1:9:1:9 | a |
| local_dataflow.rb:1:1:150:3 | <uninitialized> | local_dataflow.rb:10:9:10:9 | x |
| local_dataflow.rb:1:1:150:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a |
@@ -2870,23 +2871,41 @@
| local_dataflow.rb:9:9:9:15 | Array | local_dataflow.rb:9:9:9:15 | call to [] |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:5 | array |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:10:5:13:3 | ... | local_dataflow.rb:10:1:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | <captured entry> self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | <captured exit> x | local_dataflow.rb:15:5:15:5 | x |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:9:10:9 | x |
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:1:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | call to each | local_dataflow.rb:10:5:13:3 | ... |
| local_dataflow.rb:10:5:13:3 | synthetic *args | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
| local_dataflow.rb:10:9:10:9 | ... = ... | local_dataflow.rb:10:9:10:9 | if ... |
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [false] ! ... |
| local_dataflow.rb:10:9:10:9 | defined? ... | local_dataflow.rb:10:9:10:9 | [true] ! ... |
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | ... = ... |
| local_dataflow.rb:10:9:10:9 | nil | local_dataflow.rb:10:9:10:9 | x |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | defined? ... |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:10:9:10:9 | phi |
| local_dataflow.rb:10:9:10:9 | x | local_dataflow.rb:12:5:12:5 | x |
| local_dataflow.rb:10:14:10:18 | [post] array | local_dataflow.rb:15:10:15:14 | array |
| local_dataflow.rb:10:14:10:18 | array | local_dataflow.rb:15:10:15:14 | array |
| local_dataflow.rb:11:1:11:2 | [post] self | local_dataflow.rb:12:3:12:5 | self |
| local_dataflow.rb:11:1:11:2 | self | local_dataflow.rb:12:3:12:5 | self |
| local_dataflow.rb:15:1:17:3 | <captured exit> x | local_dataflow.rb:19:5:19:5 | x |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | ... = ... |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:1:17:3 | __synth__0__1 | local_dataflow.rb:15:5:15:5 | x |
| local_dataflow.rb:15:1:17:3 | call to each | local_dataflow.rb:15:1:17:3 | ... |
| local_dataflow.rb:15:1:17:3 | synthetic *args | local_dataflow.rb:15:1:17:3 | __synth__0__1 |
| local_dataflow.rb:15:5:15:5 | ... = ... | local_dataflow.rb:15:5:15:5 | if ... |
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [false] ! ... |
| local_dataflow.rb:15:5:15:5 | defined? ... | local_dataflow.rb:15:5:15:5 | [true] ! ... |
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | ... = ... |
| local_dataflow.rb:15:5:15:5 | nil | local_dataflow.rb:15:5:15:5 | x |
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | defined? ... |
| local_dataflow.rb:15:5:15:5 | x | local_dataflow.rb:15:5:15:5 | phi |
| local_dataflow.rb:15:10:15:14 | [post] array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:15:10:15:14 | array | local_dataflow.rb:19:10:19:14 | array |
| local_dataflow.rb:16:9:16:10 | 10 | local_dataflow.rb:16:3:16:10 | break |
@@ -2894,7 +2913,15 @@
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:1:21:3 | __synth__0__1 | local_dataflow.rb:19:5:19:5 | x |
| local_dataflow.rb:19:1:21:3 | call to each | local_dataflow.rb:19:1:21:3 | ... |
| local_dataflow.rb:19:1:21:3 | synthetic *args | local_dataflow.rb:19:1:21:3 | __synth__0__1 |
| local_dataflow.rb:19:5:19:5 | ... = ... | local_dataflow.rb:19:5:19:5 | if ... |
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [false] ! ... |
| local_dataflow.rb:19:5:19:5 | defined? ... | local_dataflow.rb:19:5:19:5 | [true] ! ... |
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | ... = ... |
| local_dataflow.rb:19:5:19:5 | nil | local_dataflow.rb:19:5:19:5 | x |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | defined? ... |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:19:5:19:5 | phi |
| local_dataflow.rb:19:5:19:5 | x | local_dataflow.rb:20:6:20:6 | x |
| local_dataflow.rb:20:6:20:6 | x | local_dataflow.rb:20:6:20:10 | ... > ... |
| local_dataflow.rb:20:10:20:10 | 1 | local_dataflow.rb:20:6:20:10 | ... > ... |

View File

@@ -124,6 +124,8 @@ definition
| ssa.rb:26:3:28:5 | <captured exit> elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:7:26:10 | phi | ssa.rb:26:7:26:10 | elem |
| ssa.rb:32:1:36:3 | self (m3) | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:16:35:5 | <captured entry> self | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
@@ -312,6 +314,7 @@ read
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | <captured entry> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
@@ -466,6 +469,7 @@ firstRead
| ssa.rb:10:5:10:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:11:10:11:10 | i |
| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | <captured entry> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
@@ -617,6 +621,7 @@ lastRead
| ssa.rb:18:1:23:3 | self (m1) | ssa.rb:18:1:23:3 | self | ssa.rb:20:5:20:10 | self |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:19:9:19:9 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:25:1:30:3 | <uninitialized> | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | <captured entry> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
@@ -732,6 +737,8 @@ phi
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:10:5:10:5 | i |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x |
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:26:7:26:10 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | <uninitialized> |
| ssa.rb:26:7:26:10 | phi | ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | <uninitialized> |
| ssa.rb:45:3:45:12 | phi | ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:3 | x |
| ssa.rb:50:3:50:8 | phi | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | <uninitialized> |

View File

@@ -226,6 +226,8 @@ variableAccess
| ssa.rb:21:5:21:5 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
| ssa.rb:26:15:26:22 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 |
| ssa.rb:27:5:27:13 | self | ssa.rb:25:1:30:3 | self | ssa.rb:25:1:30:3 | m2 |
| ssa.rb:27:10:27:13 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 |
@@ -354,6 +356,7 @@ explicitWrite
| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... -= ... |
| ssa.rb:21:5:21:5 | x | ssa.rb:21:5:21:10 | ... = ... |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:3:28:5 | ... = ... |
| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | ... = ... |
| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:9 | ... = ... |
| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:7 | ... = ... |
| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:19 | ... = ... |
@@ -567,6 +570,7 @@ readAccess
| ssa.rb:20:10:20:10 | x |
| ssa.rb:21:5:21:5 | x |
| ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:15:26:22 | elements |
| ssa.rb:27:5:27:13 | self |
| ssa.rb:27:10:27:13 | elem |

View File

@@ -33,9 +33,9 @@
| tst.rb:137:11:137:17 | (\\w\|G)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of 'G'. |
| tst.rb:143:11:143:18 | (\\d\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| tst.rb:146:11:146:17 | (\\d\|5)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '5'. |
| tst.rb:149:11:149:20 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
| tst.rb:152:11:152:24 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000b'. |
| tst.rb:155:11:155:20 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\u000c'. |
| tst.rb:149:11:149:20 | (\\s\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
| tst.rb:152:11:152:24 | (\\s\|[\\v]\|\\\\v)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000b'. |
| tst.rb:155:11:155:20 | (\\f\|[\\f])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '\\u000c'. |
| tst.rb:158:11:158:18 | (\\W\|\\D)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of ' '. |
| tst.rb:161:11:161:18 | (\\S\|\\w)* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |
| tst.rb:164:11:164:20 | (\\S\|[\\w])* | This part of the regular expression may cause exponential backtracking on strings containing many repetitions of '0'. |

View File

@@ -3,6 +3,7 @@
*/
private import codeql.regex.RegexTreeView
private import codeql.util.Numbers
/**
* Classes and predicates that create an NFA and various algorithms for working with it.
@@ -17,6 +18,20 @@ module Make<RegexTreeViewSig TreeImpl> {
exists(int code | code = ascii(c) | code + 1 = ascii(result))
}
/**
* Gets the `i`th codepoint in `s`.
*/
bindingset[s]
private string getCodepointAt(string s, int i) { result = s.regexpFind("(.|\\s)", i, _) }
/**
* Gets the length of `s` in codepoints.
*/
bindingset[str]
private int getCodepointLength(string str) {
result = str.regexpReplaceAll("(.|\\s)", "x").length()
}
/**
* Gets an approximation for the ASCII code for `char`.
* Only the easily printable chars are included (so no newline, tab, null, etc).
@@ -189,17 +204,17 @@ module Make<RegexTreeViewSig TreeImpl> {
/** An input symbol corresponding to character `c`. */
Char(string c) {
c =
any(RegexpCharacterConstant cc |
cc instanceof RelevantRegExpTerm and
not isIgnoreCase(cc.getRootTerm())
).getValue().charAt(_)
getCodepointAt(any(RegexpCharacterConstant cc |
cc instanceof RelevantRegExpTerm and
not isIgnoreCase(cc.getRootTerm())
).getValue(), _)
or
// normalize everything to lower case if the regexp is case insensitive
c =
any(RegexpCharacterConstant cc, string char |
cc instanceof RelevantRegExpTerm and
isIgnoreCase(cc.getRootTerm()) and
char = cc.getValue().charAt(_)
char = getCodepointAt(cc.getValue(), _)
|
char.toLowerCase()
)
@@ -395,7 +410,7 @@ module Make<RegexTreeViewSig TreeImpl> {
string getARelevantChar() {
exists(ascii(result))
or
exists(RegexpCharacterConstant c | result = c.getValue().charAt(_))
exists(RegexpCharacterConstant c | result = getCodepointAt(c.getValue(), _))
or
classEscapeMatches(_, result)
}
@@ -693,6 +708,12 @@ module Make<RegexTreeViewSig TreeImpl> {
)
}
pragma[noinline]
private int getCodepointLengthForState(string s) {
result = getCodepointLength(s) and
s = any(RegexpCharacterConstant reg).getValue()
}
/**
* Holds if the NFA has a transition from `q1` to `q2` labelled with `lbl`.
*/
@@ -701,16 +722,16 @@ module Make<RegexTreeViewSig TreeImpl> {
q1 = Match(s, i) and
(
not isIgnoreCase(s.getRootTerm()) and
lbl = Char(s.getValue().charAt(i))
lbl = Char(getCodepointAt(s.getValue(), i))
or
// normalize everything to lower case if the regexp is case insensitive
isIgnoreCase(s.getRootTerm()) and
exists(string c | c = s.getValue().charAt(i) | lbl = Char(c.toLowerCase()))
exists(string c | c = getCodepointAt(s.getValue(), i) | lbl = Char(c.toLowerCase()))
) and
(
q2 = Match(s, i + 1)
or
s.getValue().length() = i + 1 and
getCodepointLengthForState(s.getValue()) = i + 1 and
q2 = after(s)
)
)
@@ -811,7 +832,7 @@ module Make<RegexTreeViewSig TreeImpl> {
Match(RelevantRegExpTerm t, int i) {
i = 0
or
exists(t.(RegexpCharacterConstant).getValue().charAt(i))
exists(getCodepointAt(t.(RegexpCharacterConstant).getValue(), i))
} or
/**
* An accept state, where exactly the given input string is accepted.
@@ -1104,7 +1125,9 @@ module Make<RegexTreeViewSig TreeImpl> {
*/
predicate reachesOnlyRejectableSuffixes(State fork, string w) {
isReDoSCandidate(fork, w) and
forex(State next | next = process(fork, w, w.length() - 1) | isLikelyRejectable(next)) and
forex(State next | next = process(fork, w, getCodepointLengthForCandidate(w) - 1) |
isLikelyRejectable(next)
) and
not getProcessPrevious(fork, _, w) = acceptsAnySuffix() // we stop `process(..)` early if we can, check here if it happened.
}
@@ -1214,6 +1237,13 @@ module Make<RegexTreeViewSig TreeImpl> {
exists(string char | char = ["|", "\n", "Z"] | not deltaClosedChar(s, char, _))
}
// `process` can't use pragma[inline] predicates. So a materialized version of `getCodepointAt` is needed.
pragma[noinline]
private string getCodePointAtForProcess(string str, int i) {
result = getCodepointAt(str, i) and
isReDoSCandidate(_, str)
}
/**
* Gets a state that can be reached from pumpable `fork` consuming all
* chars in `w` any number of times followed by the first `i+1` characters of `w`.
@@ -1223,7 +1253,7 @@ module Make<RegexTreeViewSig TreeImpl> {
exists(State prev | prev = getProcessPrevious(fork, i, w) |
not prev = acceptsAnySuffix() and // we stop `process(..)` early if we can. If the successor accepts any suffix, then we know it can never be rejected.
exists(string char, InputSymbol sym |
char = w.charAt(i) and
char = getCodePointAtForProcess(w, i) and
deltaClosed(prev, sym, result) and
// noopt to prevent joining `prev` with all possible `chars` that could transition away from `prev`.
// Instead only join with the set of `chars` where a relevant `InputSymbol` has already been found.
@@ -1232,6 +1262,12 @@ module Make<RegexTreeViewSig TreeImpl> {
)
}
pragma[noinline]
private int getCodepointLengthForCandidate(string s) {
result = getCodepointLength(s) and
isReDoSCandidate(_, s)
}
/**
* Gets a state that can be reached from pumpable `fork` consuming all
* chars in `w` any number of times followed by the first `i` characters of `w`.
@@ -1245,7 +1281,7 @@ module Make<RegexTreeViewSig TreeImpl> {
or
// repeat until fixpoint
i = 0 and
result = process(fork, w, w.length() - 1)
result = process(fork, w, getCodepointLengthForCandidate(w) - 1)
)
}
@@ -1261,7 +1297,9 @@ module Make<RegexTreeViewSig TreeImpl> {
/**
* Gets a `char` that occurs in a `pump` string.
*/
private string getAProcessChar() { result = any(string s | isReDoSCandidate(_, s)).charAt(_) }
private string getAProcessChar() {
result = getCodepointAt(any(string s | isReDoSCandidate(_, s)), _)
}
}
/**
@@ -1305,10 +1343,40 @@ module Make<RegexTreeViewSig TreeImpl> {
bindingset[s]
private string escape(string s) {
result =
s.replaceAll("\\", "\\\\")
.replaceAll("\n", "\\n")
.replaceAll("\r", "\\r")
.replaceAll("\t", "\\t")
escapeUnicodeString(s.replaceAll("\\", "\\\\")
.replaceAll("\n", "\\n")
.replaceAll("\r", "\\r")
.replaceAll("\t", "\\t"))
}
/**
* Gets a string where the unicode characters in `s` have been escaped.
*/
bindingset[s]
private string escapeUnicodeString(string s) {
result =
concat(int i, string char | char = escapeUnicodeChar(getCodepointAt(s, i)) | char order by i)
}
/**
* Gets a unicode escaped string for `char`.
* If `char` is a printable char, then `char` is returned.
*/
bindingset[char]
private string escapeUnicodeChar(string char) {
if isPrintable(char)
then result = char
else
if exists(to4digitHex(any(int i | i.toUnicode() = char)))
then result = "\\u" + to4digitHex(any(int i | i.toUnicode() = char))
else result = "\\u{" + toHex(any(int i | i.toUnicode() = char)) + "}"
}
/** Holds if `char` is easily printable char, or whitespace. */
private predicate isPrintable(string char) {
exists(ascii(char))
or
char = "\n\r\t".charAt(_)
}
/**

View File

@@ -3,4 +3,5 @@ version: 0.1.3-dev
groups: shared
library: true
dependencies:
codeql/util: ${workspace}
warnOnImplicitThis: true

View File

@@ -50,7 +50,7 @@ int parseHexInt(string hex) {
sum(int index, string c |
c = stripped.charAt(index)
|
sixteenToThe(stripped.length() - 1 - index) * toHex(c)
sixteenToThe(stripped.length() - 1 - index) * charToHex(c)
)
)
}
@@ -83,7 +83,7 @@ int parseOctalInt(string octal) {
}
/** Gets the integer value of the `hex` char. */
private int toHex(string hex) {
private int charToHex(string hex) {
hex = [0 .. 9].toString() and
result = hex.toInt()
or
@@ -100,6 +100,32 @@ private int toHex(string hex) {
result = 15 and hex = ["f", "F"]
}
/**
* Gets a 4-digit hex representation of `i`.
*/
bindingset[i]
string to4digitHex(int i) {
i >= 0 and
i <= 65535 and
exists(string hex | hex = toHex(i) |
result = concat(int zeroes | zeroes = [1 .. 4 - hex.length()] | "0") + hex
)
}
/**
* Gets a hex representation of `i`.
*/
bindingset[i]
string toHex(int i) {
result =
// make the number with lots of preceding zeroes, then remove all preceding zeroes in a post-processing step
concat(int shift |
shift in [28, 24, 20, 16, 12, 8, 4, 0]
|
"0123456789abcdef".charAt(i.bitShiftRight(shift).bitAnd(15)) order by shift desc
).regexpReplaceAll("^0*", "")
}
/**
* Gets the value of 16 to the power of `n`. Holds only for `n` in the range
* 0..7 (inclusive).

View File

@@ -73,7 +73,7 @@ codeql::ForEachStmt StmtTranslator::translateForEachStmt(const swift::ForEachStm
auto entry = dispatcher.createEntry(stmt);
fillLabeledStmt(stmt, entry);
entry.body = dispatcher.fetchLabel(stmt.getBody());
entry.sequence = dispatcher.fetchLabel(stmt.getParsedSequence());
entry.sequence = dispatcher.fetchLabel(stmt.getTypeCheckedSequence());
entry.pattern = dispatcher.fetchLabel(stmt.getPattern());
entry.where = dispatcher.fetchOptionalLabel(stmt.getWhere());
return entry;

View File

@@ -347,7 +347,6 @@ lib/codeql/swift/elements/type/StructTypeConstructor.qll a784445a9bb98bb59b866af
lib/codeql/swift/elements/type/SubstitutableType.qll 78a240c6226c2167a85dce325f0f3c552364daf879c0309ebefd4787d792df23 cdc27e531f61fb50aaa9a20f5bf05c081759ac27df35e16afcdd2d1ecdac5da0
lib/codeql/swift/elements/type/SugarType.qll 0833a0f1bd26b066817f55df7a58243dbd5da69051272c38effb45653170d5c1 cbcbd68098b76d99c09e7ee43c9e7d04e1b2e860df943a520bf793e835c4db81
lib/codeql/swift/elements/type/SyntaxSugarType.qll 699fe9b4805494b62416dc86098342a725020f59a649138e6f5ba405dd536db5 a7a002cf597c3e3d0fda67111116c61a80f1e66ab8db8ddb3e189c6f15cadda6
lib/codeql/swift/elements/type/TupleType.qll 1dc14882028be534d15e348fba318c0bb1b52e692ca833987e00c9a66a1921ad 0b34c17ce9db336d0be9a869da988f31f10f754d6ffab6fa88791e508044edd2
lib/codeql/swift/elements/type/TupleTypeConstructor.qll 060633b22ee9884cb98103b380963fac62a02799461d342372cfb9cc6303d693 c9a89f695c85e7e22947287bcc32909b1f701168fd89c3598a45c97909e879f4
lib/codeql/swift/elements/type/TypeAliasTypeConstructor.qll f63ada921beb95d5f3484ab072aa4412e93adfc8e7c0b1637273f99356f5cb13 f90d2789f7c922bc8254a0d131e36b40db1e00f9b32518633520d5c3341cd70a
lib/codeql/swift/elements/type/TypeReprConstructor.qll 2bb9c5ece40c6caed9c3a614affc0efd47ad2309c09392800ad346bf369969bf 30429adc135eb8fc476bc9bc185cff0a4119ddc0e618368c44f4a43246b5287f

1
swift/ql/.gitattributes generated vendored
View File

@@ -349,7 +349,6 @@
/lib/codeql/swift/elements/type/SubstitutableType.qll linguist-generated
/lib/codeql/swift/elements/type/SugarType.qll linguist-generated
/lib/codeql/swift/elements/type/SyntaxSugarType.qll linguist-generated
/lib/codeql/swift/elements/type/TupleType.qll linguist-generated
/lib/codeql/swift/elements/type/TupleTypeConstructor.qll linguist-generated
/lib/codeql/swift/elements/type/TypeAliasTypeConstructor.qll linguist-generated
/lib/codeql/swift/elements/type/TypeReprConstructor.qll linguist-generated

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* `Type.getName` now gets the name of the type alone without any enclosing types. Use `Type.getFullName` for the old behaviour.

View File

@@ -6,18 +6,4 @@ class NominalType extends Generated::NominalType {
override Type getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }
NominalType getADerivedType() { result.getABaseType() = this }
/**
* Gets the full name of this `NominalType`. For example in:
* ```swift
* struct A {
* struct B {
* // ...
* }
* }
* ```
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
* full name of `B` is `A.B`.
*/
string getFullName() { result = this.getDeclaration().(NominalTypeDecl).getFullName() }
}

View File

@@ -1,4 +1,9 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.type.TupleType
/**
* A tuple type, for example:
* ```
* (Int, String)
* ```
*/
class TupleType extends Generated::TupleType { }

View File

@@ -6,7 +6,31 @@ private import codeql.swift.generated.type.Type
* This QL class is the root of the Swift type hierarchy.
*/
class Type extends Generated::Type {
override string toString() { result = this.getName() }
override string toString() { result = this.getFullName() }
/**
* Gets the name of this type.
*/
override string getName() {
// replace anything that looks like a full name `a.b.c` with just the
// short name `c`, by removing the `a.` and `b.` parts. Note that this
// has to be robust for tuple type names such as `(a, b.c)`.
result = super.getName().regexpReplaceAll("[^(),. ]++\\.(?!\\.)", "")
}
/**
* Gets the full name of this `Type`. For example in:
* ```swift
* struct A {
* struct B {
* // ...
* }
* }
* ```
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
* full name of `B` is `A.B`.
*/
string getFullName() { result = super.getName() }
/**
* Gets this type after any type aliases have been resolved. For example in

View File

@@ -158,7 +158,7 @@ private class ExtraStringLengthConflationSource extends StringLengthConflationSo
or
stringType = TStringUnicodeScalars()
) and
memberRef.getBase().getType().(NominalType).getName() = stringType.getName() and
memberRef.getBase().getType().(NominalType).getFullName() = stringType.getName() and
memberRef.getMember().(VarDecl).getName() = "count" and
this.asExpr() = memberRef
)
@@ -218,8 +218,8 @@ private class ExtraStringLengthConflationSink extends StringLengthConflationSink
stringType = TStringUnicodeScalars()
) and
(
call.getQualifier().getType().(NominalType).getName() = stringType.getName() or
call.getQualifier().getType().(InOutType).getObjectType().(NominalType).getName() =
call.getQualifier().getType().(NominalType).getFullName() = stringType.getName() or
call.getQualifier().getType().(InOutType).getObjectType().(NominalType).getFullName() =
stringType.getName()
) and
(

View File

@@ -127,7 +127,7 @@ private class DefaultUnsafeJsEvalAdditionalFlowStep extends UnsafeJsEvalAddition
)
or
exists(MemberRefExpr e, Expr self, VarDecl member |
self.getType().getName().matches(["Unsafe%Buffer%", "Unsafe%Pointer%"]) and
self.getType().getFullName().matches(["Unsafe%Buffer%", "Unsafe%Pointer%"]) and
member.getName() = "baseAddress"
|
e.getBase() = self and

View File

@@ -1,5 +1,5 @@
| Builtin.Int8 | getName: | Builtin.Int8 | getCanonicalType: | Builtin.Int8 | hasWidth: | yes |
| Builtin.Int16 | getName: | Builtin.Int16 | getCanonicalType: | Builtin.Int16 | hasWidth: | yes |
| Builtin.Int32 | getName: | Builtin.Int32 | getCanonicalType: | Builtin.Int32 | hasWidth: | yes |
| Builtin.Int64 | getName: | Builtin.Int64 | getCanonicalType: | Builtin.Int64 | hasWidth: | yes |
| Builtin.Word | getName: | Builtin.Word | getCanonicalType: | Builtin.Word | hasWidth: | no |
| Builtin.Int8 | getName: | Int8 | getCanonicalType: | Builtin.Int8 | hasWidth: | yes |
| Builtin.Int16 | getName: | Int16 | getCanonicalType: | Builtin.Int16 | hasWidth: | yes |
| Builtin.Int32 | getName: | Int32 | getCanonicalType: | Builtin.Int32 | hasWidth: | yes |
| Builtin.Int64 | getName: | Int64 | getCanonicalType: | Builtin.Int64 | hasWidth: | yes |
| Builtin.Word | getName: | Word | getCanonicalType: | Builtin.Word | hasWidth: | no |

View File

@@ -1,9 +1,9 @@
| Builtin.BridgeObject | BuiltinBridgeObjectType | getName: | Builtin.BridgeObject | getCanonicalType: | Builtin.BridgeObject |
| Builtin.Executor | BuiltinExecutorType | getName: | Builtin.Executor | getCanonicalType: | Builtin.Executor |
| Builtin.FPIEEE32 | BuiltinFloatType | getName: | Builtin.FPIEEE32 | getCanonicalType: | Builtin.FPIEEE32 |
| Builtin.FPIEEE64 | BuiltinFloatType | getName: | Builtin.FPIEEE64 | getCanonicalType: | Builtin.FPIEEE64 |
| Builtin.IntLiteral | BuiltinIntegerLiteralType | getName: | Builtin.IntLiteral | getCanonicalType: | Builtin.IntLiteral |
| Builtin.Job | BuiltinJobType | getName: | Builtin.Job | getCanonicalType: | Builtin.Job |
| Builtin.NativeObject | BuiltinNativeObjectType | getName: | Builtin.NativeObject | getCanonicalType: | Builtin.NativeObject |
| Builtin.RawPointer | BuiltinRawPointerType | getName: | Builtin.RawPointer | getCanonicalType: | Builtin.RawPointer |
| Builtin.RawUnsafeContinuation | BuiltinRawUnsafeContinuationType | getName: | Builtin.RawUnsafeContinuation | getCanonicalType: | Builtin.RawUnsafeContinuation |
| Builtin.BridgeObject | BuiltinBridgeObjectType | getName: | BridgeObject | getCanonicalType: | Builtin.BridgeObject |
| Builtin.Executor | BuiltinExecutorType | getName: | Executor | getCanonicalType: | Builtin.Executor |
| Builtin.FPIEEE32 | BuiltinFloatType | getName: | FPIEEE32 | getCanonicalType: | Builtin.FPIEEE32 |
| Builtin.FPIEEE64 | BuiltinFloatType | getName: | FPIEEE64 | getCanonicalType: | Builtin.FPIEEE64 |
| Builtin.IntLiteral | BuiltinIntegerLiteralType | getName: | IntLiteral | getCanonicalType: | Builtin.IntLiteral |
| Builtin.Job | BuiltinJobType | getName: | Job | getCanonicalType: | Builtin.Job |
| Builtin.NativeObject | BuiltinNativeObjectType | getName: | NativeObject | getCanonicalType: | Builtin.NativeObject |
| Builtin.RawPointer | BuiltinRawPointerType | getName: | RawPointer | getCanonicalType: | Builtin.RawPointer |
| Builtin.RawUnsafeContinuation | BuiltinRawUnsafeContinuationType | getName: | RawUnsafeContinuation | getCanonicalType: | Builtin.RawUnsafeContinuation |

View File

@@ -1,5 +1,5 @@
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (Builtin.IntLiteral, Builtin.IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (Builtin.IntLiteral, Builtin.IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (IntLiteral, IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
| (Builtin.IntLiteral, Builtin.IntLiteral) | getName: | (IntLiteral, IntLiteral) | getCanonicalType: | (Builtin.IntLiteral, Builtin.IntLiteral) | getNumberOfTypes: | 2 |
| (Int, Int, Int, Int, Int) | getName: | (Int, Int, Int, Int, Int) | getCanonicalType: | (Int, Int, Int, Int, Int) | getNumberOfTypes: | 5 |
| (Int, String, Double) | getName: | (Int, String, Double) | getCanonicalType: | (Int, String, Double) | getNumberOfTypes: | 3 |
| (Int, s: String, Double) | getName: | (Int, s: String, Double) | getCanonicalType: | (Int, s: String, Double) | getNumberOfTypes: | 3 |

Some files were not shown because too many files have changed in this diff Show More