Merge branch 'main' into redsun82/lfs

This commit is contained in:
Paolo Tranquilli
2024-05-02 18:05:50 +02:00
29 changed files with 797 additions and 691 deletions

View File

@@ -1665,3 +1665,311 @@ class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
/** Gets the second-level scope containing the node `n`, if any. */
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
/**
* Module that defines flow through iterators.
* For example,
* ```cpp
* auto it = v.begin();
* *it = source();
* ...
* sink(v[0]);
* ```
*/
module IteratorFlow {
private import codeql.ssa.Ssa as SsaImpl
private import semmle.code.cpp.models.interfaces.Iterator as Interface
private import semmle.code.cpp.models.implementations.Iterator as Impl
/**
* A variable of some type that can produce an iterator.
*/
class SourceVariable extends Ssa::SourceVariable {
SourceVariable() {
exists(Interface::GetIteratorFunction gets, Cpp::FunctionInput input, int i |
input.isParameterDerefOrQualifierObject(i) and
gets.getsIterator(input, _)
|
this.getType().stripType() = gets.getParameter(i).getType().stripType()
or
i = -1 and
this.getType().stripType() = gets.getDeclaringType()
)
}
}
private module SsaInput implements SsaImpl::InputSig<Location> {
import Ssa::InputSigCommon
class SourceVariable = IteratorFlow::SourceVariable;
/** A call to function that dereferences an iterator. */
private class IteratorPointerDereferenceCall extends CallInstruction {
IteratorPointerDereferenceCall() {
this.getStaticCallTarget() instanceof Impl::IteratorPointerDereferenceOperator
}
}
/** A call to a function that obtains an iterator. */
private class GetsIteratorCall extends CallInstruction {
GetsIteratorCall() { this.getStaticCallTarget() instanceof Impl::GetIteratorFunction }
}
/** A call to `operator++` or `operator--` on an iterator. */
private class IteratorCrementCall extends CallInstruction {
IteratorCrementCall() { this.getStaticCallTarget() instanceof Impl::IteratorCrementOperator }
}
/**
* Gets an ultimate definition of `def`.
*
* Note: Unlike `def.getAnUltimateDefinition()` this predicate also
* traverses back through iterator increment and decrement operations.
*/
private Ssa::Def getAnUltimateDefinition(Ssa::Def def) {
result = def.getAnUltimateDefinition()
or
exists(IRBlock bb, int i, IteratorCrementCall crementCall, Ssa::SourceVariable sv |
crementCall = def.getValue().asInstruction().(StoreInstruction).getSourceValue() and
sv = def.getSourceVariable() and
bb.getInstruction(i) = crementCall and
Ssa::ssaDefReachesRead(sv, result.asDef(), bb, i)
)
}
/**
* Holds if `write` is an instruction that writes to address `address`
*/
private predicate isIteratorWrite(Instruction write, Operand address) {
exists(Ssa::DefImpl writeDef, IRBlock bb, int i |
writeDef.hasIndexInBlock(bb, i, _) and
bb.getInstruction(i) = write and
address = writeDef.getAddressOperand()
)
}
/**
* Holds if `writeToDeref` is a write to an iterator that was obtained
* by `beginCall`. That is, the following instruction sequence holds:
* ```cpp
* it = container.begin(); // or a similar iterator-obtaining function call
* ...
* *it = value;
* ```
*/
private predicate isIteratorStoreInstruction(
GetsIteratorCall beginCall, Instruction writeToDeref
) {
exists(
StoreInstruction beginStore, IRBlock bbStar, int iStar, Ssa::Def def,
IteratorPointerDereferenceCall starCall, Ssa::Def ultimate, Operand address
|
isIteratorWrite(writeToDeref, address) and
operandForFullyConvertedCall(address, starCall) and
bbStar.getInstruction(iStar) = starCall and
Ssa::ssaDefReachesRead(_, def.asDef(), bbStar, iStar) and
ultimate = getAnUltimateDefinition*(def) and
beginStore = ultimate.getValue().asInstruction() and
operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall)
)
}
/**
* Holds if `(bb, i)` contains a write to an iterator that may have been obtained
* by calling `begin` (or related functions) on the variable `v`.
*/
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
certain = false and
exists(GetsIteratorCall beginCall, Instruction writeToDeref, IRBlock bbQual, int iQual |
isIteratorStoreInstruction(beginCall, writeToDeref) and
bb.getInstruction(i) = writeToDeref and
bbQual.getInstruction(iQual) = beginCall and
Ssa::variableRead(bbQual, iQual, v, _)
)
}
/** Holds if `(bb, i)` reads the container variable `v`. */
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
Ssa::variableRead(bb, i, v, certain)
}
}
private module IteratorSsa = SsaImpl::Make<Location, SsaInput>;
cached
private newtype TSsaDef =
TDef(IteratorSsa::DefinitionExt def) or
TPhi(PhiNode phi)
abstract private class SsaDef extends TSsaDef {
/** Gets a textual representation of this element. */
string toString() { none() }
/** Gets the underlying non-phi definition or use. */
IteratorSsa::DefinitionExt asDef() { none() }
/** Gets the underlying phi node. */
PhiNode asPhi() { none() }
/** Gets the location of this element. */
abstract Location getLocation();
}
private class Def extends TDef, SsaDef {
IteratorSsa::DefinitionExt def;
Def() { this = TDef(def) }
final override IteratorSsa::DefinitionExt asDef() { result = def }
final override Location getLocation() { result = this.getImpl().getLocation() }
/** Gets the variable written to by this definition. */
final SourceVariable getSourceVariable() { result = def.getSourceVariable() }
override string toString() { result = def.toString() }
/**
* Holds if this definition (or use) has index `index` in block `block`,
* and is a definition (or use) of the variable `sv`.
*/
predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
def.definesAt(sv, block, index, _)
}
private Ssa::DefImpl getImpl() {
exists(IRBlock bb, int i |
this.hasIndexInBlock(bb, i, _) and
result.hasIndexInBlock(bb, i)
)
}
/** Gets the value written by this definition (i.e., the "right-hand side"). */
Node0Impl getValue() { result = this.getImpl().getValue() }
/** Gets the indirection index of this definition. */
int getIndirectionIndex() { result = this.getImpl().getIndirectionIndex() }
}
private class Phi extends TPhi, SsaDef {
PhiNode phi;
Phi() { this = TPhi(phi) }
final override PhiNode asPhi() { result = phi }
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
override string toString() { result = phi.toString() }
SsaIteratorNode getNode() { result.getIteratorFlowNode() = phi }
}
private class PhiNode extends IteratorSsa::DefinitionExt {
PhiNode() {
this instanceof IteratorSsa::PhiNode or
this instanceof IteratorSsa::PhiReadNode
}
SsaIteratorNode getNode() { result.getIteratorFlowNode() = this }
}
cached
private module IteratorSsaCached {
cached
predicate adjacentDefRead(IRBlock bb1, int i1, SourceVariable sv, IRBlock bb2, int i2) {
IteratorSsa::adjacentDefReadExt(_, sv, bb1, i1, bb2, i2)
or
exists(PhiNode phi |
IteratorSsa::lastRefRedefExt(_, sv, bb1, i1, phi) and
phi.definesAt(sv, bb2, i2, _)
)
}
cached
Node getAPriorDefinition(IteratorSsa::DefinitionExt next) {
exists(IRBlock bb, int i, SourceVariable sv, IteratorSsa::DefinitionExt def |
IteratorSsa::lastRefRedefExt(pragma[only_bind_into](def), pragma[only_bind_into](sv),
pragma[only_bind_into](bb), pragma[only_bind_into](i), next) and
nodeToDefOrUse(result, sv, bb, i, _)
)
}
}
/** The set of nodes necessary for iterator flow. */
class IteratorFlowNode instanceof PhiNode {
/** Gets a textual representation of this node. */
string toString() { result = super.toString() }
/** Gets the type of this node. */
DataFlowType getType() {
exists(Ssa::SourceVariable sv |
super.definesAt(sv, _, _, _) and
result = sv.getType()
)
}
/** Gets the `Declaration` that contains this block. */
Declaration getFunction() { result = super.getBasicBlock().getEnclosingFunction() }
/** Gets the locatino of this node. */
Location getLocation() { result = super.getBasicBlock().getLocation() }
}
private import IteratorSsaCached
private predicate defToNode(Node node, Def def, boolean uncertain) {
(
nodeHasOperand(node, def.getValue().asOperand(), def.getIndirectionIndex())
or
nodeHasInstruction(node, def.getValue().asInstruction(), def.getIndirectionIndex())
) and
uncertain = false
}
private predicate nodeToDefOrUse(
Node node, SourceVariable sv, IRBlock bb, int i, boolean uncertain
) {
exists(Def def |
def.hasIndexInBlock(bb, i, sv) and
defToNode(node, def, uncertain)
)
or
useToNode(bb, i, sv, node) and
uncertain = false
}
private predicate useToNode(IRBlock bb, int i, SourceVariable sv, Node nodeTo) {
exists(PhiNode phi |
phi.definesAt(sv, bb, i, _) and
nodeTo = phi.getNode()
)
or
exists(Ssa::UseImpl use |
use.hasIndexInBlock(bb, i, sv) and
nodeTo = use.getNode()
)
}
/**
* Holds if `nodeFrom` flows to `nodeTo` in a single step.
*/
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
exists(
Node nFrom, SourceVariable sv, IRBlock bb1, int i1, IRBlock bb2, int i2, boolean uncertain
|
adjacentDefRead(bb1, i1, sv, bb2, i2) and
nodeToDefOrUse(nFrom, sv, bb1, i1, uncertain) and
useToNode(bb2, i2, sv, nodeTo)
|
if uncertain = true
then
nodeFrom =
[
nFrom,
getAPriorDefinition(any(IteratorSsa::DefinitionExt next | next.definesAt(sv, bb1, i1, _)))
]
else nFrom = nodeFrom
)
}
}

View File

@@ -46,6 +46,7 @@ private newtype TIRDataFlowNode =
Ssa::isModifiableByCall(operand, indirectionIndex)
} or
TSsaPhiNode(Ssa::PhiNode phi) or
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
} or
@@ -653,6 +654,30 @@ class SsaPhiNode extends Node, TSsaPhiNode {
predicate isPhiRead() { phi.isPhiRead() }
}
/**
* INTERNAL: do not use.
*
* Dataflow nodes necessary for iterator flow
*/
class SsaIteratorNode extends Node, TSsaIteratorNode {
IteratorFlow::IteratorFlowNode node;
SsaIteratorNode() { this = TSsaIteratorNode(node) }
/** Gets the phi node associated with this node. */
IteratorFlow::IteratorFlowNode getIteratorFlowNode() { result = node }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Declaration getFunction() { result = node.getFunction() }
override DataFlowType getType() { result = node.getType() }
final override Location getLocationImpl() { result = node.getLocation() }
override string toStringImpl() { result = node.toString() }
}
/**
* INTERNAL: do not use.
*
@@ -1190,11 +1215,11 @@ class UninitializedNode extends Node {
LocalVariable v;
UninitializedNode() {
exists(Ssa::Def def |
exists(Ssa::Def def, Ssa::SourceVariable sv |
def.getIndirectionIndex() = 0 and
def.getValue().asInstruction() instanceof UninitializedInstruction and
Ssa::nodeToDefOrUse(this, def, _) and
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
Ssa::defToNode(this, def, sv, _, _, _) and
v = sv.getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
)
}
@@ -2151,6 +2176,8 @@ private module Cached {
// Def-use/Use-use flow
Ssa::ssaFlow(nodeFrom, nodeTo)
or
IteratorFlow::localFlowStep(nodeFrom, nodeTo)
or
// Operand -> Instruction flow
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
or

View File

@@ -102,12 +102,20 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
}
cached
private newtype TDefOrUseImpl =
private newtype TDefImpl =
TDefAddressImpl(BaseIRVariable v) or
TDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) {
TDirectDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) {
isDef(_, _, address, base, _, indirectionIndex)
} or
TUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) {
TGlobalDefImpl(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents the initial "definition" of a global variable when entering
// a function body.
isGlobalDefImpl(v, f, _, indirectionIndex)
}
cached
private newtype TUseImpl =
TDirectUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) {
isUse(_, operand, base, _, indirectionIndex) and
not isDef(true, _, operand, _, _, _)
} or
@@ -116,21 +124,6 @@ private newtype TDefOrUseImpl =
// the assignment to a global variable isn't ruled out as dead.
isGlobalUse(v, f, _, indirectionIndex)
} or
TGlobalDefImpl(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
// Represents the initial "definition" of a global variable when entering
// a function body.
isGlobalDefImpl(v, f, _, indirectionIndex)
} or
TIteratorDef(
Operand iteratorDerefAddress, BaseSourceVariableInstruction container, int indirectionIndex
) {
isIteratorDef(container, iteratorDerefAddress, _, _, indirectionIndex)
} or
TIteratorUse(
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
) {
isIteratorUse(container, iteratorAddress, _, indirectionIndex)
} or
TFinalParameterUse(Parameter p, int indirectionIndex) {
underlyingTypeIsModifiableAt(p.getUnderlyingType(), indirectionIndex) and
// Only create an SSA read for the final use of a parameter if there's
@@ -177,7 +170,84 @@ private predicate underlyingTypeIsModifiableAt(Type underlying, int indirectionI
private Indirection getIndirectionForUnspecifiedType(Type t) { result.getType() = t }
abstract private class DefOrUseImpl extends TDefOrUseImpl {
abstract class DefImpl extends TDefImpl {
int indirectionIndex;
bindingset[indirectionIndex]
DefImpl() { any() }
/** Gets a textual representation of this element. */
abstract string toString();
/** Gets the block of this definition or use. */
final IRBlock getBlock() { this.hasIndexInBlock(result, _) }
/** Holds if this definition or use has index `index` in block `block`. */
abstract predicate hasIndexInBlock(IRBlock block, int index);
/**
* Holds if this definition (or use) has index `index` in block `block`,
* and is a definition (or use) of the variable `sv`
*/
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
this.hasIndexInBlock(block, index) and
sv = this.getSourceVariable()
}
/** Gets the location of this element. */
abstract Cpp::Location getLocation();
/** Gets the indirection index of this definition. */
final int getIndirectionIndex() { result = indirectionIndex }
/**
* Gets the index (i.e., the number of loads required) of this
* definition or use.
*
* Note that this is _not_ the definition's (or use's) index in
* the enclosing basic block. To obtain this index, use
* `DefOrUseImpl::hasIndexInBlock/2` or `DefOrUseImpl::hasIndexInBlock/3`.
*/
abstract int getIndirection();
/**
* Gets the instruction that computes the base of this definition or use.
* This is always a `VariableAddressInstruction` or an `CallInstruction`.
*/
abstract BaseSourceVariableInstruction getBase();
/**
* Gets the base source variable (i.e., the variable without
* any indirection) of this definition or use.
*/
final BaseSourceVariable getBaseSourceVariable() {
this.getBase().getBaseSourceVariable() = result
}
/** Gets the variable that is defined or used. */
SourceVariable getSourceVariable() {
exists(BaseSourceVariable v, int indirection |
sourceVariableHasBaseAndIndex(result, v, indirection) and
defHasSourceVariable(this, v, indirection)
)
}
abstract predicate isCertain();
abstract Node0Impl getValue();
Operand getAddressOperand() { none() }
}
abstract class UseImpl extends TUseImpl {
int indirectionIndex;
bindingset[indirectionIndex]
UseImpl() { any() }
/** Gets the node associated with this use. */
abstract Node getNode();
/** Gets a textual representation of this element. */
abstract string toString();
@@ -207,7 +277,10 @@ abstract private class DefOrUseImpl extends TDefOrUseImpl {
* the enclosing basic block. To obtain this index, use
* `DefOrUseImpl::hasIndexInBlock/2` or `DefOrUseImpl::hasIndexInBlock/3`.
*/
abstract int getIndirectionIndex();
abstract int getIndirection();
/** Gets the indirection index of this use. */
final int getIndirectionIndex() { result = indirectionIndex }
/**
* Gets the instruction that computes the base of this definition or use.
@@ -225,17 +298,17 @@ abstract private class DefOrUseImpl extends TDefOrUseImpl {
/** Gets the variable that is defined or used. */
SourceVariable getSourceVariable() {
exists(BaseSourceVariable v, int ind |
sourceVariableHasBaseAndIndex(result, v, ind) and
defOrUseHasSourceVariable(this, v, ind)
exists(BaseSourceVariable v, int indirection |
sourceVariableHasBaseAndIndex(result, v, indirection) and
useHasSourceVariable(this, v, indirection)
)
}
}
private predicate defOrUseHasSourceVariable(DefOrUseImpl defOrUse, BaseSourceVariable bv, int ind) {
defHasSourceVariable(defOrUse, bv, ind)
or
useHasSourceVariable(defOrUse, bv, ind)
/**
* Holds if this use is guaranteed to read the
* associated variable.
*/
abstract predicate isCertain();
}
pragma[noinline]
@@ -256,32 +329,17 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari
v.getIndirection() = ind
}
abstract class DefImpl extends DefOrUseImpl {
int ind;
bindingset[ind]
DefImpl() { any() }
override int getIndirectionIndex() { result = ind }
override string toString() { result = "Def of " + this.getSourceVariable() }
abstract int getIndirection();
abstract predicate isCertain();
abstract Node0Impl getValue();
}
/** An initial definition of an `IRVariable`'s address. */
private class DefAddressImpl extends DefImpl, TDefAddressImpl {
BaseIRVariable v;
DefAddressImpl() {
this = TDefAddressImpl(v) and
ind = 0
indirectionIndex = 0
}
override string toString() { result = "Def of &" + v.toString() }
final override int getIndirection() { result = 0 }
final override predicate isCertain() { any() }
@@ -303,91 +361,45 @@ private class DefAddressImpl extends DefImpl, TDefAddressImpl {
final override BaseSourceVariableInstruction getBase() { none() }
}
/**
* An SSA definition that has an associated `Operand` representing the address
* that is being written to.
*/
abstract private class OperandBasedDef extends DefImpl {
private class DirectDef extends DefImpl, TDirectDefImpl {
Operand address;
BaseSourceVariableInstruction base;
bindingset[ind]
OperandBasedDef() { any() }
Operand getAddressOperand() { result = address }
DirectDef() { this = TDirectDefImpl(base, address, indirectionIndex) }
override Cpp::Location getLocation() { result = this.getAddressOperand().getUse().getLocation() }
final override predicate hasIndexInBlock(IRBlock block, int index) {
this.getAddressOperand().getUse() = block.getInstruction(index)
}
}
private class DirectDef extends OperandBasedDef, TDefImpl {
BaseSourceVariableInstruction base;
override string toString() { result = "Def of " + this.getSourceVariable() }
DirectDef() { this = TDefImpl(base, address, ind) }
override Operand getAddressOperand() { result = address }
override BaseSourceVariableInstruction getBase() { result = base }
override int getIndirection() { isDef(_, _, address, base, result, ind) }
override int getIndirection() { isDef(_, _, address, base, result, indirectionIndex) }
override Node0Impl getValue() { isDef(_, result, address, base, _, _) }
override predicate isCertain() { isDef(true, _, address, base, _, ind) }
override predicate isCertain() { isDef(true, _, address, base, _, indirectionIndex) }
}
private class IteratorDef extends OperandBasedDef, TIteratorDef {
BaseSourceVariableInstruction container;
IteratorDef() { this = TIteratorDef(address, container, ind) }
override BaseSourceVariableInstruction getBase() { result = container }
override int getIndirection() { isIteratorDef(container, address, _, result, ind) }
override Node0Impl getValue() { isIteratorDef(container, address, result, _, _) }
override predicate isCertain() { none() }
}
abstract class UseImpl extends DefOrUseImpl {
int ind;
bindingset[ind]
UseImpl() { any() }
/** Gets the node associated with this use. */
abstract Node getNode();
override string toString() { result = "Use of " + this.getSourceVariable() }
/** Gets the indirection index of this use. */
final override int getIndirectionIndex() { result = ind }
/** Gets the number of loads that precedence this use. */
abstract int getIndirection();
/**
* Holds if this use is guaranteed to read the
* associated variable.
*/
abstract predicate isCertain();
}
abstract private class OperandBasedUse extends UseImpl {
private class DirectUseImpl extends UseImpl, TDirectUseImpl {
Operand operand;
BaseSourceVariableInstruction base;
bindingset[ind]
OperandBasedUse() { any() }
DirectUseImpl() { this = TDirectUseImpl(base, operand, indirectionIndex) }
override string toString() { result = "Use of " + this.getSourceVariable() }
final override predicate hasIndexInBlock(IRBlock block, int index) {
// See the comment in `ssa0`'s `OperandBasedUse` for an explanation of this
// predicate's implementation.
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
then
exists(Operand op, int indirectionIndex, int indirection |
indirectionIndex = this.getIndirectionIndex() and
exists(Operand op, int indirection |
indirection = this.getIndirection() and
op =
min(Operand cand, int i |
@@ -406,26 +418,12 @@ abstract private class OperandBasedUse extends UseImpl {
final Operand getOperand() { result = operand }
final override Cpp::Location getLocation() { result = operand.getLocation() }
}
private class DirectUse extends OperandBasedUse, TUseImpl {
DirectUse() { this = TUseImpl(base, operand, ind) }
override int getIndirection() { isUse(_, operand, base, result, indirectionIndex) }
override int getIndirection() { isUse(_, operand, base, result, ind) }
override predicate isCertain() { isUse(true, operand, base, _, indirectionIndex) }
override predicate isCertain() { isUse(true, operand, base, _, ind) }
override Node getNode() { nodeHasOperand(result, operand, ind) }
}
private class IteratorUse extends OperandBasedUse, TIteratorUse {
IteratorUse() { this = TIteratorUse(operand, base, ind) }
override int getIndirection() { isIteratorUse(base, operand, result, ind) }
override predicate isCertain() { none() }
override Node getNode() { nodeHasOperand(result, operand, ind) }
override Node getNode() { nodeHasOperand(result, operand, indirectionIndex) }
}
pragma[nomagic]
@@ -439,15 +437,17 @@ private predicate finalParameterNodeHasParameterAndIndex(
class FinalParameterUse extends UseImpl, TFinalParameterUse {
Parameter p;
FinalParameterUse() { this = TFinalParameterUse(p, ind) }
FinalParameterUse() { this = TFinalParameterUse(p, indirectionIndex) }
override string toString() { result = "Use of " + p.toString() }
Parameter getParameter() { result = p }
int getArgumentIndex() { result = p.getIndex() }
override Node getNode() { finalParameterNodeHasParameterAndIndex(result, p, ind) }
override Node getNode() { finalParameterNodeHasParameterAndIndex(result, p, indirectionIndex) }
override int getIndirection() { result = ind + 1 }
override int getIndirection() { result = indirectionIndex + 1 }
override predicate isCertain() { any() }
@@ -544,11 +544,13 @@ class GlobalUse extends UseImpl, TGlobalUse {
GlobalLikeVariable global;
IRFunction f;
GlobalUse() { this = TGlobalUse(global, f, ind) }
GlobalUse() { this = TGlobalUse(global, f, indirectionIndex) }
override string toString() { result = "Use of " + global }
override FinalGlobalValue getNode() { result.getGlobalUse() = this }
override int getIndirection() { isGlobalUse(global, f, result, ind) }
override int getIndirection() { isGlobalUse(global, f, result, indirectionIndex) }
/** Gets the global variable associated with this use. */
GlobalLikeVariable getVariable() { result = global }
@@ -598,10 +600,9 @@ class GlobalUse extends UseImpl, TGlobalUse {
*
* See the QLDoc for `GlobalUse` for how this is used.
*/
class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
class GlobalDefImpl extends DefImpl, TGlobalDefImpl {
GlobalLikeVariable global;
IRFunction f;
int indirectionIndex;
GlobalDefImpl() { this = TGlobalDefImpl(global, f, indirectionIndex) }
@@ -611,9 +612,6 @@ class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
/** Gets the `IRFunction` whose body is evaluated after this definition. */
IRFunction getIRFunction() { result = f }
/** Gets the global variable associated with this definition. */
override int getIndirectionIndex() { result = indirectionIndex }
/** Holds if this definition or use has index `index` in block `block`. */
final override predicate hasIndexInBlock(IRBlock block, int index) {
exists(EnterFunctionInstruction enter |
@@ -627,7 +625,11 @@ class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
sourceVariableIsGlobal(result, global, f, this.getIndirection())
}
int getIndirection() { result = indirectionIndex }
override int getIndirection() { result = indirectionIndex }
override Node0Impl getValue() { none() }
override predicate isCertain() { any() }
/**
* Gets the type of this definition after specifiers have been deeply
@@ -648,60 +650,30 @@ class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
}
/**
* Holds if `defOrUse1` is a definition which is first read by `use`,
* or if `defOrUse1` is a use and `use` is a next subsequent use.
*
* In both cases, `use` can either be an explicit use written in the
* source file, or it can be a phi node as computed by the SSA library.
* Holds if there is a definition or access at index `i1` in basic block `bb1`
* and the next subsequent read is at index `i2` in basic block `bb2`.
*/
predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
exists(IRBlock bb1, int i1, SourceVariable v |
defOrUse1
.asDefOrUse()
.hasIndexInBlock(pragma[only_bind_out](bb1), pragma[only_bind_out](i1),
pragma[only_bind_out](v))
|
exists(IRBlock bb2, int i2, DefinitionExt def |
adjacentDefReadExt(pragma[only_bind_into](def), pragma[only_bind_into](bb1),
pragma[only_bind_into](i1), pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) and
def.getSourceVariable() = v and
use.asDefOrUse().(UseImpl).hasIndexInBlock(bb2, i2, v)
)
or
exists(PhiNode phi |
lastRefRedefExt(_, bb1, i1, phi) and
use.asPhi() = phi and
phi.getSourceVariable() = pragma[only_bind_into](v)
)
predicate adjacentDefRead(IRBlock bb1, int i1, SourceVariable sv, IRBlock bb2, int i2) {
adjacentDefReadExt(_, sv, bb1, i1, bb2, i2)
or
exists(PhiNode phi |
lastRefRedefExt(_, sv, bb1, i1, phi) and
phi.definesAt(sv, bb2, i2, _)
)
}
/**
* Holds if `globalDef` represents the initial definition of a global variable that
* flows to `useOrPhi`.
*/
private predicate globalDefToUse(GlobalDef globalDef, UseOrPhi useOrPhi) {
exists(IRBlock bb1, int i1, SourceVariable v |
globalDef
.hasIndexInBlock(pragma[only_bind_out](bb1), pragma[only_bind_out](i1),
pragma[only_bind_out](v))
|
exists(IRBlock bb2, int i2 |
adjacentDefReadExt(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) and
useOrPhi.asDefOrUse().hasIndexInBlock(bb2, i2, v)
)
or
exists(PhiNode phi |
lastRefRedefExt(_, bb1, i1, phi) and
useOrPhi.asPhi() = phi and
phi.getSourceVariable() = pragma[only_bind_into](v)
)
predicate useToNode(IRBlock bb, int i, SourceVariable sv, Node nodeTo) {
exists(Phi phi |
phi.asPhi().definesAt(sv, bb, i, _) and
nodeTo = phi.getNode()
)
or
exists(UseImpl use |
use.hasIndexInBlock(bb, i, sv) and
nodeTo = use.getNode()
)
}
private predicate useToNode(UseOrPhi use, Node nodeTo) { use.getNode() = nodeTo }
pragma[noinline]
predicate outNodeHasAddressAndIndex(
IndirectArgumentOutNode out, Operand address, int indirectionIndex
@@ -710,11 +682,19 @@ predicate outNodeHasAddressAndIndex(
out.getIndirectionIndex() = indirectionIndex
}
private predicate defToNode(Node nodeFrom, Def def, boolean uncertain) {
/**
* INTERNAL: Do not use.
*
* Holds if `node` is the node that corresponds to the definition of `def`.
*/
predicate defToNode(Node node, Def def, SourceVariable sv, IRBlock bb, int i, boolean uncertain) {
def.hasIndexInBlock(bb, i, sv) and
(
nodeHasOperand(nodeFrom, def.getValue().asOperand(), def.getIndirectionIndex())
nodeHasOperand(node, def.getValue().asOperand(), def.getIndirectionIndex())
or
nodeHasInstruction(nodeFrom, def.getValue().asInstruction(), def.getIndirectionIndex())
nodeHasInstruction(node, def.getValue().asInstruction(), def.getIndirectionIndex())
or
node.(InitialGlobalValue).getGlobalDef() = def
) and
if def.isCertain() then uncertain = false else uncertain = true
}
@@ -722,14 +702,16 @@ private predicate defToNode(Node nodeFrom, Def def, boolean uncertain) {
/**
* INTERNAL: Do not use.
*
* Holds if `nodeFrom` is the node that correspond to the definition or use `defOrUse`.
* Holds if `node` is the node that corresponds to the definition or use at
* index `i` in block `bb` of `sv`.
*
* `uncertain` is `true` if this is an uncertain definition.
*/
predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse, boolean uncertain) {
// Node -> Def
defToNode(nodeFrom, defOrUse, uncertain)
predicate nodeToDefOrUse(Node node, SourceVariable sv, IRBlock bb, int i, boolean uncertain) {
defToNode(node, _, sv, bb, i, uncertain)
or
// Node -> Use
useToNode(defOrUse, nodeFrom) and
useToNode(bb, i, sv, node) and
uncertain = false
}
@@ -738,9 +720,9 @@ predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse, boolean uncertain)
* only holds when there is no use-use relation out of `nTo`.
*/
private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
not exists(UseOrPhi defOrUse |
nodeToDefOrUse(nTo, defOrUse, _) and
adjacentDefRead(defOrUse, _)
not exists(SourceVariable sv, IRBlock bb2, int i2 |
nodeToDefOrUse(nTo, sv, bb2, i2, _) and
adjacentDefRead(bb2, i2, sv, _, _)
) and
(
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
@@ -774,60 +756,39 @@ private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
* So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the
* first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`.
*/
private predicate adjustForPointerArith(PostUpdateNode pun, UseOrPhi use) {
exists(DefOrUse defOrUse, Node adjusted |
private predicate adjustForPointerArith(PostUpdateNode pun, SourceVariable sv, IRBlock bb2, int i2) {
exists(IRBlock bb1, int i1, Node adjusted |
indirectConversionFlowStep*(adjusted, pun.getPreUpdateNode()) and
nodeToDefOrUse(adjusted, defOrUse, _) and
adjacentDefRead(defOrUse, use)
nodeToDefOrUse(adjusted, sv, bb1, i1, _) and
adjacentDefRead(bb1, i1, sv, bb2, i2)
)
}
/**
* Holds if `nodeFrom` flows to `nodeTo` because there is `def-use` or
* `use-use` flow from `defOrUse` to `use`.
* Holds if there should be flow from `nodeFrom` to `nodeTo` because
* `nodeFrom` is a definition or use of `sv` at index `i1` at basic
* block `bb1`.
*
* `uncertain` is `true` if the `defOrUse` is an uncertain definition.
* `uncertain` is `true` if `(bb1, i1)` is a definition, and that definition
* is _not_ guaranteed to overwrite the entire allocation.
*/
private predicate localSsaFlow(
SsaDefOrUse defOrUse, Node nodeFrom, UseOrPhi use, Node nodeTo, boolean uncertain
private predicate ssaFlowImpl(
IRBlock bb1, int i1, SourceVariable sv, Node nodeFrom, Node nodeTo, boolean uncertain
) {
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
adjacentDefRead(defOrUse, use) and
useToNode(use, nodeTo) and
exists(IRBlock bb2, int i2 |
nodeToDefOrUse(nodeFrom, sv, bb1, i1, uncertain) and
adjacentDefRead(bb1, i1, sv, bb2, i2) and
useToNode(bb2, i2, sv, nodeTo)
) and
nodeFrom != nodeTo
}
private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo, boolean uncertain) {
exists(UseOrPhi use |
localSsaFlow(defOrUse, nodeFrom, use, nodeTo, uncertain)
or
// Initial global variable value to a first use
nodeFrom.(InitialGlobalValue).getGlobalDef() = defOrUse and
globalDefToUse(defOrUse, use) and
useToNode(use, nodeTo) and
uncertain = false
)
}
/**
* Holds if `def` is the corresponding definition of
* the SSA library's `definition`.
*/
private DefinitionExt ssaDefinition(Def def) {
exists(IRBlock block, int i, SourceVariable sv |
def.hasIndexInBlock(block, i, sv) and
result.definesAt(sv, block, i, _)
)
}
/** Gets a node that represents the prior definition of `node`. */
private Node getAPriorDefinition(SsaDefOrUse defOrUse) {
exists(IRBlock bb, int i, SourceVariable sv, DefinitionExt def, DefOrUse defOrUse0 |
lastRefRedefExt(pragma[only_bind_into](def), pragma[only_bind_into](bb),
pragma[only_bind_into](i), ssaDefinition(defOrUse)) and
def.getSourceVariable() = sv and
defOrUse0.hasIndexInBlock(bb, i, sv) and
nodeToDefOrUse(result, defOrUse0, _)
private Node getAPriorDefinition(DefinitionExt next) {
exists(IRBlock bb, int i, SourceVariable sv |
lastRefRedefExt(_, pragma[only_bind_into](sv), pragma[only_bind_into](bb),
pragma[only_bind_into](i), next) and
nodeToDefOrUse(result, sv, bb, i, _)
)
}
@@ -879,12 +840,16 @@ private predicate modeledFlowBarrier(Node n) {
/** Holds if there is def-use or use-use flow from `nodeFrom` to `nodeTo`. */
predicate ssaFlow(Node nodeFrom, Node nodeTo) {
exists(Node nFrom, boolean uncertain, SsaDefOrUse defOrUse |
ssaFlowImpl(defOrUse, nFrom, nodeTo, uncertain) and
exists(Node nFrom, boolean uncertain, IRBlock bb, int i, SourceVariable sv |
ssaFlowImpl(bb, i, sv, nFrom, nodeTo, uncertain) and
not modeledFlowBarrier(nFrom) and
nodeFrom != nodeTo
|
if uncertain = true then nodeFrom = [nFrom, getAPriorDefinition(defOrUse)] else nodeFrom = nFrom
if uncertain = true
then
nodeFrom =
[nFrom, getAPriorDefinition(any(DefinitionExt next | next.definesAt(sv, bb, i, _)))]
else nodeFrom = nFrom
)
}
@@ -929,15 +894,15 @@ private predicate isArgumentOfCallable(DataFlowCall call, Node n) {
* Holds if there is use-use flow from `pun`'s pre-update node to `n`.
*/
private predicate postUpdateNodeToFirstUse(PostUpdateNode pun, Node n) {
exists(UseOrPhi use |
adjustForPointerArith(pun, use) and
useToNode(use, n)
exists(SourceVariable sv, IRBlock bb2, int i2 |
adjustForPointerArith(pun, sv, bb2, i2) and
useToNode(bb2, i2, sv, n)
)
}
private predicate stepUntilNotInCall(DataFlowCall call, Node n1, Node n2) {
isArgumentOfCallable(call, n1) and
exists(Node mid | localSsaFlow(_, n1, _, mid, _) |
exists(Node mid | ssaFlowImpl(_, _, _, n1, mid, _) |
isArgumentOfCallable(call, mid) and
stepUntilNotInCall(call, mid, n2)
or
@@ -984,35 +949,13 @@ predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
)
}
/**
* Holds if `use` is a use of `sv` and is a next adjacent use of `phi` in
* index `i1` in basic block `bb1`.
*
* This predicate exists to prevent an early join of `adjacentDefRead` with `definesAt`.
*/
pragma[nomagic]
private predicate fromPhiNodeToUse(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, UseOrPhi use) {
exists(IRBlock bb2, int i2 |
use.asDefOrUse().hasIndexInBlock(bb2, i2, sv) and
adjacentDefReadExt(pragma[only_bind_into](phi), pragma[only_bind_into](bb1),
pragma[only_bind_into](i1), pragma[only_bind_into](bb2), pragma[only_bind_into](i2))
)
}
/** Holds if `nodeTo` receives flow from the phi node `nodeFrom`. */
predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, UseOrPhi use |
exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, IRBlock bb2, int i2 |
phi = nodeFrom.getPhiNode() and
phi.definesAt(sv, bb1, i1, _) and
useToNode(use, nodeTo)
|
fromPhiNodeToUse(phi, sv, bb1, i1, use)
or
exists(PhiNode phiTo |
phi != phiTo and
lastRefRedefExt(phi, bb1, i1, phiTo) and
nodeTo.(SsaPhiNode).getPhiNode() = phiTo
)
adjacentDefRead(bb1, i1, sv, bb2, i2) and
useToNode(bb2, i2, sv, nodeTo)
)
}
@@ -1077,8 +1020,10 @@ module SsaCached {
* path between them without any read of `def`.
*/
cached
predicate adjacentDefReadExt(DefinitionExt def, IRBlock bb1, int i1, IRBlock bb2, int i2) {
SsaImpl::adjacentDefReadExt(def, _, bb1, i1, bb2, i2)
predicate adjacentDefReadExt(
DefinitionExt def, SourceVariable sv, IRBlock bb1, int i1, IRBlock bb2, int i2
) {
SsaImpl::adjacentDefReadExt(def, sv, bb1, i1, bb2, i2)
}
/**
@@ -1087,32 +1032,38 @@ module SsaCached {
* without passing through another read or write.
*/
cached
predicate lastRefRedefExt(DefinitionExt def, IRBlock bb, int i, DefinitionExt next) {
SsaImpl::lastRefRedefExt(def, _, bb, i, next)
predicate lastRefRedefExt(
DefinitionExt def, SourceVariable sv, IRBlock bb, int i, DefinitionExt next
) {
SsaImpl::lastRefRedefExt(def, sv, bb, i, next)
}
cached
Definition phiHasInputFromBlock(PhiNode phi, IRBlock bb) {
SsaImpl::phiHasInputFromBlock(phi, result, bb)
}
cached
predicate ssaDefReachesRead(SourceVariable v, Definition def, IRBlock bb, int i) {
SsaImpl::ssaDefReachesRead(v, def, bb, i)
}
predicate variableRead = SsaInput::variableRead/4;
predicate variableWrite = SsaInput::variableWrite/4;
}
cached
private newtype TSsaDefOrUse =
TDefOrUse(DefOrUseImpl defOrUse) {
defOrUse instanceof UseImpl
or
// Like in the pruning stage, we only include definition that's live after the
// write as the final definitions computed by SSA.
exists(DefinitionExt def, SourceVariable sv, IRBlock bb, int i |
def.definesAt(sv, bb, i, _) and
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
)
} or
TPhi(PhiNode phi) or
TGlobalDef(GlobalDefImpl global)
private newtype TSsaDef =
TDef(DefinitionExt def) or
TPhi(PhiNode phi)
abstract private class SsaDefOrUse extends TSsaDefOrUse {
abstract private class SsaDef extends TSsaDef {
/** Gets a textual representation of this element. */
string toString() { none() }
/** Gets the underlying non-phi definition or use. */
DefOrUseImpl asDefOrUse() { none() }
DefinitionExt asDef() { none() }
/** Gets the underlying phi node. */
PhiNode asPhi() { none() }
@@ -1121,52 +1072,97 @@ abstract private class SsaDefOrUse extends TSsaDefOrUse {
abstract Location getLocation();
}
class DefOrUse extends TDefOrUse, SsaDefOrUse {
DefOrUseImpl defOrUse;
abstract class Def extends SsaDef, TDef {
DefinitionExt def;
DefOrUse() { this = TDefOrUse(defOrUse) }
Def() { this = TDef(def) }
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
final override DefinitionExt asDef() { result = def }
final override Location getLocation() { result = defOrUse.getLocation() }
/** Gets the source variable underlying this SSA definition. */
final SourceVariable getSourceVariable() { result = def.getSourceVariable() }
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
override string toString() { result = defOrUse.toString() }
override string toString() { result = def.toString() }
/**
* Holds if this definition (or use) has index `index` in block `block`,
* and is a definition (or use) of the variable `sv`.
*/
predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
defOrUse.hasIndexInBlock(block, index, sv)
def.definesAt(sv, block, index, _)
}
/** Gets the value written by this definition, if any. */
Node0Impl getValue() { none() }
/**
* Holds if this definition is guaranteed to overwrite the entire
* destination's allocation.
*/
abstract predicate isCertain();
/** Gets the address operand written to by this definition. */
Operand getAddressOperand() { none() }
/** Gets the address written to by this definition. */
final Instruction getAddress() { result = this.getAddressOperand().getDef() }
/** Gets the indirection index of this definition. */
abstract int getIndirectionIndex();
/**
* Gets the indirection level that this definition is writing to.
* For instance, `x = y` is a definition of `x` at indirection level 1 and
* `*x = y` is a definition of `x` at indirection level 2.
*/
abstract int getIndirection();
/**
* Gets a definition that ultimately defines this SSA definition and is not
* itself a phi node.
*/
Def getAnUltimateDefinition() { result.asDef() = def.getAnUltimateDefinition() }
}
class GlobalDef extends TGlobalDef, SsaDefOrUse {
private predicate isGlobal(DefinitionExt def, GlobalDefImpl global) {
exists(SourceVariable sv, IRBlock bb, int i |
def.definesAt(sv, bb, i, _) and
global.hasIndexInBlock(bb, i, sv)
)
}
private class NonGlobalDef extends Def {
NonGlobalDef() { not isGlobal(def, _) }
final override Location getLocation() { result = this.getImpl().getLocation() }
private DefImpl getImpl() {
exists(SourceVariable sv, IRBlock bb, int i |
this.hasIndexInBlock(bb, i, sv) and
result.hasIndexInBlock(bb, i, sv)
)
}
override Node0Impl getValue() { result = this.getImpl().getValue() }
override predicate isCertain() { this.getImpl().isCertain() }
override Operand getAddressOperand() { result = this.getImpl().getAddressOperand() }
override int getIndirectionIndex() { result = this.getImpl().getIndirectionIndex() }
override int getIndirection() { result = this.getImpl().getIndirection() }
}
class GlobalDef extends Def {
GlobalDefImpl global;
GlobalDef() { this = TGlobalDef(global) }
/** Gets the location of this definition. */
final override Location getLocation() { result = global.getLocation() }
GlobalDef() { isGlobal(def, global) }
/** Gets a textual representation of this definition. */
override string toString() { result = global.toString() }
/**
* Holds if this definition has index `index` in block `block`, and
* is a definition of the variable `sv`.
*/
predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
global.hasIndexInBlock(block, index, sv)
}
/** Gets the indirection index of this definition. */
int getIndirection() { result = global.getIndirection() }
/** Gets the indirection index of this definition. */
int getIndirectionIndex() { result = global.getIndirectionIndex() }
final override Location getLocation() { result = global.getLocation() }
/**
* Gets the type of this definition after specifiers have been deeply stripped
@@ -1184,9 +1180,15 @@ class GlobalDef extends TGlobalDef, SsaDefOrUse {
/** Gets the global variable associated with this definition. */
GlobalLikeVariable getVariable() { result = global.getVariable() }
override predicate isCertain() { any() }
final override int getIndirectionIndex() { result = global.getIndirectionIndex() }
final override int getIndirection() { result = global.getIndirection() }
}
class Phi extends TPhi, SsaDefOrUse {
class Phi extends TPhi, SsaDef {
PhiNode phi;
Phi() { this = TPhi(phi) }
@@ -1198,64 +1200,19 @@ class Phi extends TPhi, SsaDefOrUse {
override string toString() { result = "Phi" }
SsaPhiNode getNode() { result.getPhiNode() = phi }
}
class UseOrPhi extends SsaDefOrUse {
UseOrPhi() {
this.asDefOrUse() instanceof UseImpl
or
this instanceof Phi
}
predicate hasInputFromBlock(Definition inp, IRBlock bb) { inp = phiHasInputFromBlock(phi, bb) }
final override Location getLocation() {
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
}
final Node getNode() {
result = this.(Phi).getNode()
or
result = this.asDefOrUse().(UseImpl).getNode()
}
}
class Def extends DefOrUse {
override DefImpl defOrUse;
Operand getAddressOperand() { result = defOrUse.(OperandBasedDef).getAddressOperand() }
Instruction getAddress() { result = this.getAddressOperand().getDef() }
/**
* Gets the indirection index of this definition.
*
* This predicate ensures that joins go from `defOrUse` to the result
* instead of the other way around.
*/
pragma[inline]
int getIndirectionIndex() {
pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirectionIndex()
}
/**
* Gets the indirection level that this definition is writing to.
* For instance, `x = y` is a definition of `x` at indirection level 1 and
* `*x = y` is a definition of `x` at indirection level 2.
*
* This predicate ensures that joins go from `defOrUse` to the result
* instead of the other way around.
*/
pragma[inline]
int getIndirection() {
pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirection()
}
Node0Impl getValue() { result = defOrUse.getValue() }
predicate isCertain() { defOrUse.isCertain() }
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
}
private module SsaImpl = SsaImplCommon::Make<Location, SsaInput>;
/**
* An static single assignment (SSA) phi node.
*
* This is either a normal phi node or a phi-read node.
*/
class PhiNode extends SsaImpl::DefinitionExt {
PhiNode() {
this instanceof SsaImpl::PhiNode or
@@ -1269,10 +1226,30 @@ class PhiNode extends SsaImpl::DefinitionExt {
* on reads instead of writes.
*/
predicate isPhiRead() { this instanceof SsaImpl::PhiReadNode }
/** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */
predicate hasInputFromBlock(Definition inp, IRBlock bb) {
inp = SsaCached::phiHasInputFromBlock(this, bb)
}
/** Gets a definition that is an input to this phi node. */
final Definition getAnInput() { this.hasInputFromBlock(result, _) }
}
class DefinitionExt = SsaImpl::DefinitionExt;
/** An static single assignment (SSA) definition. */
class DefinitionExt extends SsaImpl::DefinitionExt {
private Definition getAPhiInputOrPriorDefinition() { result = this.(PhiNode).getAnInput() }
class UncertainWriteDefinition = SsaImpl::UncertainWriteDefinition;
/**
* Gets a definition that ultimately defines this SSA definition and is
* not itself a phi node.
*/
final DefinitionExt getAnUltimateDefinition() {
result = this.getAPhiInputOrPriorDefinition*() and
not result instanceof PhiNode
}
}
class Definition = SsaImpl::Definition;
import SsaCached

View File

@@ -246,14 +246,6 @@ private module IteratorIndirections {
baseType = super.getValueType()
}
override predicate isAdditionalDereference(Instruction deref, Operand address) {
exists(CallInstruction call |
operandForFullyConvertedCall(getAUse(deref), call) and
this = call.getStaticCallTarget().getClassAndName("operator*") and
address = call.getThisArgumentOperand()
)
}
override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) {
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
this = call.getStaticCallTarget().getClassAndName("operator=") and
@@ -262,16 +254,6 @@ private module IteratorIndirections {
)
}
override predicate isAdditionalTaintStep(Node node1, Node node2) {
exists(CallInstruction call |
// Taint through `operator+=` and `operator-=` on iterators.
call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and
node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and
node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and
node1.getType().getUnspecifiedType() = this
)
}
override predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
// This is a bit annoying: Consider the following snippet:
// ```
@@ -589,230 +571,6 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
cached
private module Cached {
private import semmle.code.cpp.models.interfaces.Iterator as Interfaces
private import semmle.code.cpp.models.implementations.Iterator as Iterator
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as IO
/**
* Holds if `next` is a instruction with a memory result that potentially
* updates the memory produced by `prev`.
*/
private predicate memorySucc(Instruction prev, Instruction next) {
prev = next.(ChiInstruction).getTotal()
or
// Phi inputs can be inexact.
prev = next.(PhiInstruction).getAnInputOperand().getAnyDef()
or
prev = next.(CopyInstruction).getSourceValue()
or
exists(ReadSideEffectInstruction read |
next = read.getPrimaryInstruction() and
isAdditionalConversionFlow(_, next) and
prev = read.getSideEffectOperand().getAnyDef()
)
}
/**
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
* that is used for a write operation that writes the value `value`. The `memory` instruction
* represents the memory that the IR's SSA analysis determined was read by the call to `operator*`.
*
* The `numberOfLoads` integer represents the number of dereferences this write corresponds to
* on the underlying container that produced the iterator.
*/
private predicate isChiAfterIteratorDef(
Instruction memory, Operand iteratorDerefAddress, Node0Impl value, int numberOfLoads
) {
exists(
BaseSourceVariableInstruction iteratorBase, ReadSideEffectInstruction read,
Operand iteratorAddress
|
numberOfLoads >= 0 and
isDef(_, value, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
iteratorBase.getResultType() instanceof Interfaces::Iterator and
isDereference(iteratorAddress.getDef(), read.getArgumentDef().getAUse(), _) and
memory = read.getSideEffectOperand().getAnyDef()
)
}
private predicate isSource(Instruction instr, Operand iteratorAddress, int numberOfLoads) {
getAUse(instr) = iteratorAddress and
exists(BaseSourceVariableInstruction iteratorBase |
iteratorBase.getResultType() instanceof Interfaces::Iterator and
not iteratorBase.getResultType() instanceof Cpp::PointerType and
isUse(_, iteratorAddress, iteratorBase, numberOfLoads - 1, 0)
)
}
private predicate isSink(Instruction instr, CallInstruction call) {
getAUse(instr).(ArgumentOperand).getCall() = call and
// Only include operations that may modify the object that the iterator points to.
// The following is a non-exhaustive list of things that may modify the value of the
// iterator, but never the value of what the iterator points to.
// The more things we can exclude here, the faster the small dataflow-like analysis
// done by `convertsIntoArgument` will converge.
not exists(Function f | f = call.getStaticCallTarget() |
f instanceof Iterator::IteratorCrementOperator or
f instanceof Iterator::IteratorBinaryArithmeticOperator or
f instanceof Iterator::IteratorAssignArithmeticOperator or
f instanceof Iterator::IteratorCrementMemberOperator or
f instanceof Iterator::IteratorBinaryArithmeticMemberOperator or
f instanceof Iterator::IteratorAssignArithmeticMemberOperator or
f instanceof Iterator::IteratorAssignmentMemberOperator
)
}
private predicate convertsIntoArgumentFwd(Instruction instr) {
isSource(instr, _, _)
or
exists(Instruction prev | convertsIntoArgumentFwd(prev) |
conversionFlow(unique( | | getAUse(prev)), instr, false, _)
)
}
private predicate convertsIntoArgumentRev(Instruction instr) {
convertsIntoArgumentFwd(instr) and
(
isSink(instr, _)
or
exists(Instruction next | convertsIntoArgumentRev(next) |
conversionFlow(unique( | | getAUse(instr)), next, false, _)
)
)
}
private predicate convertsIntoArgument(
Operand iteratorAddress, CallInstruction call, int numberOfLoads
) {
exists(Instruction iteratorAddressDef |
isSource(iteratorAddressDef, iteratorAddress, numberOfLoads) and
isSink(iteratorAddressDef, call) and
convertsIntoArgumentRev(pragma[only_bind_into](iteratorAddressDef))
)
}
private predicate isChiAfterIteratorArgument(
Instruction memory, Operand iteratorAddress, int numberOfLoads
) {
// Ideally, `iteratorAddress` would be an `ArgumentOperand`, but there might be
// various conversions applied to it before it becomes an argument.
// So we do a small amount of flow to find the call that the iterator is passed to.
exists(CallInstruction call | convertsIntoArgument(iteratorAddress, call, numberOfLoads) |
exists(ReadSideEffectInstruction read |
read.getPrimaryInstruction() = call and
read.getSideEffectOperand().getAnyDef() = memory
)
or
exists(LoadInstruction load |
iteratorAddress.getDef() = load and
memory = load.getSourceValueOperand().getAnyDef()
)
)
}
/**
* Holds if `iterator` is a `StoreInstruction` that stores the result of some function
* returning an iterator into an address computed started at `containerBase`.
*
* For example, given a declaration like `std::vector<int>::iterator it = v.begin()`,
* the `iterator` will be the `StoreInstruction` generated by the write to `it`, and
* `containerBase` will be the address of `v`.
*/
private predicate isChiAfterBegin(
BaseSourceVariableInstruction containerBase, StoreInstruction iterator
) {
exists(
CallInstruction getIterator, Iterator::GetIteratorFunction getIteratorFunction,
IO::FunctionInput input, int i
|
getIterator = iterator.getSourceValue() and
getIteratorFunction = getIterator.getStaticCallTarget() and
getIteratorFunction.getsIterator(input, _) and
isDef(_, any(Node0Impl n | n.asInstruction() = iterator), _, _, 1, 0) and
input.isParameterDerefOrQualifierObject(i) and
isUse(_, getIterator.getArgumentOperand(i), containerBase, 0, 0)
)
}
/**
* Holds if `iteratorAddress` is an address of an iterator that is used for
* a read operation. The `memory` instruction represents the memory that
* the IR's SSA analysis determined was read by the call to `operator*`.
*
* Finally, the `numberOfLoads` integer represents the number of dereferences
* this read corresponds to on the underlying container that produced the iterator.
*/
private predicate isChiBeforeIteratorUse(
Operand iteratorAddress, Instruction memory, int numberOfLoads
) {
exists(
BaseSourceVariableInstruction iteratorBase, LoadInstruction load,
ReadSideEffectInstruction read, Operand iteratorDerefAddress
|
numberOfLoads >= 0 and
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
isUse(_, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
iteratorBase.getResultType() instanceof Interfaces::Iterator and
load.getSourceAddressOperand() = iteratorDerefAddress and
read.getPrimaryInstruction() = load.getSourceAddress() and
memory = read.getSideEffectOperand().getAnyDef()
)
}
/**
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
* that is used for a write operation that writes the value `value` to a container that
* created the iterator. `container` represents the base of the address of the container
* that was used to create the iterator.
*/
cached
predicate isIteratorDef(
BaseSourceVariableInstruction container, Operand iteratorDerefAddress, Node0Impl value,
int numberOfLoads, int indirectionIndex
) {
exists(Instruction memory, Instruction begin, int upper, int ind |
isChiAfterIteratorDef(memory, iteratorDerefAddress, value, numberOfLoads) and
memorySucc*(begin, memory) and
isChiAfterBegin(container, begin) and
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
ind = numberOfLoads + [1 .. upper] and
indirectionIndex = ind - (numberOfLoads + 1)
)
}
/**
* Holds if `iteratorAddress` is an address of an iterator that is used for a
* read operation to read a value from a container that created the iterator.
* `container` represents the base of the address of the container that was used
* to create the iterator.
*/
cached
predicate isIteratorUse(
BaseSourceVariableInstruction container, Operand iteratorAddress, int numberOfLoads,
int indirectionIndex
) {
// Direct use
exists(Instruction begin, Instruction memory, int upper, int ind |
isChiBeforeIteratorUse(iteratorAddress, memory, numberOfLoads) and
memorySucc*(begin, memory) and
isChiAfterBegin(container, begin) and
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
ind = numberOfLoads + [1 .. upper] and
indirectionIndex = ind - (numberOfLoads + 1)
)
or
// Use through function output
exists(Instruction memory, Instruction begin, int upper, int ind |
isChiAfterIteratorArgument(memory, iteratorAddress, numberOfLoads) and
memorySucc*(begin, memory) and
isChiAfterBegin(container, begin) and
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
ind = numberOfLoads + [1 .. upper] and
indirectionIndex = ind - (numberOfLoads - 1)
)
}
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
private predicate isConversion(Operand op) {
exists(Instruction def, Operand use |

View File

@@ -560,7 +560,7 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
TaintFunction, SideEffectFunction, AliasFunction
{
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(0) and
(input.isParameterDeref(0) or input.isParameter(0)) and
output.isQualifierObject()
}
@@ -579,17 +579,34 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
override predicate parameterEscapesOnlyViaReturn(int index) { index = -1 }
}
/**
* A `begin` member function, or a related function, that returns an iterator.
*/
class BeginFunction extends MemberFunction {
BeginFunction() {
this.hasName(["begin", "cbegin", "rbegin", "crbegin", "before_begin", "cbefore_begin"]) and
this.getType().getUnspecifiedType() instanceof Iterator
}
}
/**
* An `end` member function, or a related function, that returns an iterator.
*/
class EndFunction extends MemberFunction {
EndFunction() {
this.hasName(["end", "cend", "rend", "crend"]) and
this.getType().getUnspecifiedType() instanceof Iterator
}
}
/**
* A `begin` or `end` member function, or a related member function, that
* returns an iterator.
*/
class BeginOrEndFunction extends MemberFunction {
BeginOrEndFunction() {
this.hasName([
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
"cbefore_begin"
]) and
this.getType().getUnspecifiedType() instanceof Iterator
this instanceof BeginFunction or
this instanceof EndFunction
}
}

View File

@@ -228,7 +228,6 @@ irFlow
| test.cpp:333:17:333:22 | call to source | test.cpp:337:10:337:18 | globalVar |
| test.cpp:333:17:333:22 | call to source | test.cpp:339:10:339:18 | globalVar |
| test.cpp:333:17:333:22 | call to source | test.cpp:343:10:343:18 | globalVar |
| test.cpp:333:17:333:22 | call to source | test.cpp:349:10:349:18 | globalVar |
| test.cpp:347:17:347:22 | call to source | test.cpp:337:10:337:18 | globalVar |
| test.cpp:347:17:347:22 | call to source | test.cpp:339:10:339:18 | globalVar |
| test.cpp:347:17:347:22 | call to source | test.cpp:343:10:343:18 | globalVar |
@@ -260,7 +259,6 @@ irFlow
| test.cpp:562:17:562:31 | *call to indirect_source | test.cpp:566:10:566:19 | * ... |
| test.cpp:562:17:562:31 | *call to indirect_source | test.cpp:568:10:568:19 | * ... |
| test.cpp:562:17:562:31 | *call to indirect_source | test.cpp:572:10:572:19 | * ... |
| test.cpp:562:17:562:31 | *call to indirect_source | test.cpp:578:10:578:19 | * ... |
| test.cpp:576:17:576:31 | *call to indirect_source | test.cpp:566:10:566:19 | * ... |
| test.cpp:576:17:576:31 | *call to indirect_source | test.cpp:568:10:568:19 | * ... |
| test.cpp:576:17:576:31 | *call to indirect_source | test.cpp:572:10:572:19 | * ... |

View File

@@ -346,7 +346,7 @@ namespace FlowThroughGlobals {
void taintAndCall() {
globalVar = source();
calledAfterTaint();
sink(globalVar); // $ ast ir=333:17 ir=347:17
sink(globalVar); // $ ast ir
}
}
@@ -575,7 +575,7 @@ namespace IndirectFlowThroughGlobals {
void taintAndCall() {
globalInt = indirect_source();
calledAfterTaint();
sink(*globalInt); // $ ir=562:17 ir=576:17 MISSING: ast=562:17 ast=576:17
sink(*globalInt); // $ ir MISSING: ast=562:17 ast=576:17
}
}

View File

@@ -66,8 +66,8 @@ public:
insert_iterator_by_trait operator++(int);
insert_iterator_by_trait &operator--();
insert_iterator_by_trait operator--(int);
insert_iterator_by_trait operator*();
insert_iterator_by_trait operator=(int x);
insert_iterator_by_trait& operator*();
insert_iterator_by_trait& operator=(int x);
};
template<>

View File

@@ -389,7 +389,7 @@ void test_vector_output_iterator(int b) {
*i9 = source();
taint_vector_output_iterator(i9);
sink(v9); // $ ast=330:10 MISSING: ir SPURIOUS: ast=389:8
sink(v9); // $ ast=330:10 ir SPURIOUS: ast=389:8
std::vector<int>::iterator i10 = v10.begin();
vector_iterator_assign_wrapper(i10, 10);
@@ -440,14 +440,14 @@ void test_vector_inserter(char *source_string) {
std::vector<std::string> out;
auto it = std::back_inserter(out);
*++it = std::string(source_string);
sink(out); // $ ast MISSING: ir
sink(out); // $ ast,ir
}
{
std::vector<int> out;
auto it = std::back_inserter(out);
*++it = source();
sink(out); // $ ast MISSING: ir
sink(out); // $ ast,ir
}
}

View File

@@ -4,12 +4,6 @@ uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
| builtin.c:5:5:5:11 | (no string representation) | Node should have one toString but has 0. |
| misc.c:227:7:227:28 | (no string representation) | Node should have one toString but has 0. |
| static_init_templates.cpp:80:18:80:23 | (no string representation) | Node should have one toString but has 0. |
| static_init_templates.cpp:80:18:80:23 | (no string representation) | Node should have one toString but has 0. |
| static_init_templates.cpp:89:18:89:23 | (no string representation) | Node should have one toString but has 0. |
| static_init_templates.cpp:89:18:89:23 | (no string representation) | Node should have one toString but has 0. |
parameterCallable
localFlowIsLocal
readStepIsLocal

View File

@@ -77,7 +77,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// "json.net"
/// }
/// </summary>
private void AddPackageDependencies(JObject json)
private void AddPackageDependencies(JObject json, string jsonPath)
{
// If there is more than one framework we need to pick just one.
// To ensure stability we pick one based on the lexicographic order of
@@ -91,7 +91,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
if (references is null)
{
logger.LogDebug("No references found in the targets section in the assets file.");
logger.LogDebug($"No references found in the targets section in '{jsonPath}'");
return;
}
@@ -154,7 +154,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// "microsoft.netcore.app.ref"
/// }
/// </summary>
private void AddFrameworkDependencies(JObject json)
private void AddFrameworkDependencies(JObject json, string jsonPath)
{
var frameworks = json
@@ -163,7 +163,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
if (frameworks is null)
{
logger.LogDebug("No framework section in assets.json.");
logger.LogDebug($"No framework section in '{jsonPath}'.");
return;
}
@@ -177,7 +177,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
if (references is null)
{
logger.LogDebug("No framework references in assets.json.");
logger.LogDebug($"No framework references in '{jsonPath}'.");
return;
}
@@ -196,8 +196,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
try
{
var obj = JObject.Parse(json);
AddPackageDependencies(obj);
AddFrameworkDependencies(obj);
AddPackageDependencies(obj, json);
AddFrameworkDependencies(obj, json);
return true;
}
catch (Exception e)

View File

@@ -94,7 +94,10 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
public HashSet<AssemblyLookupLocation> Restore()
{
var assemblyLookupLocations = new HashSet<AssemblyLookupLocation>();
var checkNugetFeedResponsiveness = EnvironmentVariables.GetBoolean(EnvironmentVariableNames.CheckNugetFeedResponsiveness);
var checkNugetFeedResponsiveness = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.CheckNugetFeedResponsiveness);
logger.LogInfo($"Checking NuGet feed responsiveness: {checkNugetFeedResponsiveness}");
compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", checkNugetFeedResponsiveness ? "1" : "0"));
try
{
if (checkNugetFeedResponsiveness && !CheckFeeds())

View File

@@ -20,17 +20,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
protected override bool IsEnabled()
{
var webViewExtractionOption = Environment.GetEnvironmentVariable(EnvironmentVariableNames.WebViewGeneration);
if (webViewExtractionOption == null ||
bool.TryParse(webViewExtractionOption, out var shouldExtractWebViews) &&
shouldExtractWebViews)
{
compilationInfoContainer.CompilationInfos.Add(("WebView extraction enabled", "1"));
return true;
}
compilationInfoContainer.CompilationInfos.Add(("WebView extraction enabled", "0"));
return false;
var webViewExtractionOption = EnvironmentVariables.GetBooleanOptOut(EnvironmentVariableNames.WebViewGeneration);
compilationInfoContainer.CompilationInfos.Add(("WebView extraction enabled", webViewExtractionOption ? "1" : "0"));
return webViewExtractionOption;
}
protected override ICollection<string> AdditionalFiles => fileProvider.RazorViews;

View File

@@ -29,6 +29,19 @@ namespace Semmle.Util
return threads;
}
public static bool GetBooleanOptOut(string name)
{
var env = Environment.GetEnvironmentVariable(name);
if (env == null ||
bool.TryParse(env, out var value) &&
value)
{
return true;
}
return false;
}
public static bool GetBoolean(string name)
{
var env = Environment.GetEnvironmentVariable(name);

View File

@@ -1,5 +1,7 @@
| All Nuget feeds reachable | 1.0 |
| Failed project restore with package source error | 0.0 |
| Failed solution restore with package source error | 0.0 |
| NuGet feed responsiveness checked | 1.0 |
| Project files on filesystem | 1.0 |
| Resource extraction enabled | 1.0 |
| Restored .NET framework variants | 1.0 |

View File

@@ -1,6 +1,8 @@
| All Nuget feeds reachable | 1.0 |
| Failed project restore with package source error | 1.0 |
| Failed solution restore with package source error | 0.0 |
| Fallback nuget restore | 1.0 |
| NuGet feed responsiveness checked | 1.0 |
| Project files on filesystem | 1.0 |
| Resolved assembly conflicts | 7.0 |
| Resource extraction enabled | 0.0 |

View File

@@ -1,6 +1,7 @@
| All Nuget feeds reachable | 0.0 |
| Fallback nuget restore | 1.0 |
| Inherited Nuget feed count | 1.0 |
| NuGet feed responsiveness checked | 1.0 |
| Project files on filesystem | 1.0 |
| Resolved assembly conflicts | 7.0 |
| Resource extraction enabled | 0.0 |

View File

@@ -2,7 +2,7 @@ from create_database_utils import *
from diagnostics_test_utils import *
import os
os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK"] = "true" # Enable NuGet feed check
# os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK"] = "true" # Nuget feed check is enabled by default
os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK_TIMEOUT"] = "1" # 1ms, the GET request should fail with such short timeout
os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK_LIMIT"] = "1" # Limit the count of checks to 1
os.environ["CODEQL_EXTRACTOR_CSHARP_BUILDLESS_NUGET_FEEDS_CHECK_EXCLUDED"] = "https://abc.de:8000/packages/" # Exclude this feed from check

View File

@@ -1,4 +1,6 @@
extensions:
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/csharp-all
extensible: sourceModel

View File

@@ -1,5 +1,6 @@
extensions:
# Make sure that the extensible model predicates are at least defined as empty.
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/go-all
extensible: sourceModel

View File

@@ -1,5 +1,6 @@
extensions:
# Make sure that the extensible model predicates are at least defined as empty.
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/java-all
extensible: sourceModel
@@ -15,4 +16,4 @@ extensions:
- addsTo:
pack: codeql/java-all
extensible: neutralModel
data: []
data: []

View File

@@ -1,6 +1,6 @@
# Define the extensible prediactes related to experimental queries
# to at least be empty.
extensions:
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/java-all
extensible: experimentalSourceModel

View File

@@ -1,5 +1,6 @@
extensions:
# Contribute empty data sets to avoid errors about an undefined extensionals
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/javascript-all
extensible: sourceModel

View File

@@ -1,5 +1,6 @@
extensions:
# Contribute empty data sets to avoid errors about an undefined extensionals
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/python-all
extensible: sourceModel

View File

@@ -19,9 +19,11 @@ class StringSubstitutionCall extends DataFlow::CallNode {
StringSubstitutionCall() {
this.getMethodName() = ["sub", "sub!", "gsub", "gsub!"] and
exists(this.getReceiver()) and
this.getNumberOfArguments() = 2
or
this.getNumberOfArguments() = 1 and exists(this.getBlock())
(
this.getNumberOfArguments() = 2
or
this.getNumberOfArguments() = 1 and exists(this.getBlock())
)
}
/**

View File

@@ -1,5 +1,6 @@
extensions:
# Contribute empty data sets to avoid errors about an undefined extensionals
# Make sure that the extensible model predicates have at least one definition
# to avoid errors about undefined extensionals.
- addsTo:
pack: codeql/ruby-all
extensible: sourceModel

View File

@@ -29,4 +29,4 @@ where
// does NOT hold.
if disablingNode.getLocation() = origin.getLocation() then ending = "." else ending = " by $@."
select request, "This request may run without certificate validation because $@" + ending,
disablingNode, "the request is disabled", origin, "this value"
disablingNode, "validation is disabled", origin, "this value"

View File

@@ -268,3 +268,8 @@ def bad_path_sanitizer(p1, p2)
p1.sub! "/../", "" # NOT OK
p2.sub "/../", "" # NOT OK
end
def each_line_sanitizer(p1)
p1.each_line("\n") do |l| # OK - does no sanitization
end
end

View File

@@ -1,28 +1,28 @@
| Excon.rb:6:3:6:34 | call to get | This request may run without certificate validation because $@. | Excon.rb:5:38:5:42 | ... = ... | the request is disabled | Excon.rb:5:38:5:42 | false | this value |
| Excon.rb:12:3:12:34 | call to get | This request may run without certificate validation because $@. | Excon.rb:11:27:11:31 | ... = ... | the request is disabled | Excon.rb:11:27:11:31 | false | this value |
| Excon.rb:18:3:18:34 | call to get | This request may run without certificate validation because $@ by $@. | Excon.rb:17:38:17:60 | ... = ... | the request is disabled | Excon.rb:17:55:17:59 | false | this value |
| Excon.rb:24:3:24:10 | call to get | This request may run without certificate validation because $@. | Excon.rb:23:72:23:76 | false | the request is disabled | Excon.rb:23:72:23:76 | false | this value |
| Excon.rb:30:3:30:62 | call to get | This request may run without certificate validation because $@. | Excon.rb:30:53:30:57 | false | the request is disabled | Excon.rb:30:53:30:57 | false | this value |
| Faraday.rb:5:12:5:30 | call to get | This request may run without certificate validation because $@. | Faraday.rb:4:63:4:67 | false | the request is disabled | Faraday.rb:4:63:4:67 | false | this value |
| Faraday.rb:9:12:9:30 | call to get | This request may run without certificate validation because $@. | Faraday.rb:8:68:8:92 | VERIFY_NONE | the request is disabled | Faraday.rb:8:68:8:92 | VERIFY_NONE | this value |
| Faraday.rb:35:16:35:35 | call to get | This request may run without certificate validation because $@ by $@. | Faraday.rb:34:51:34:53 | arg | the request is disabled | Faraday.rb:38:42:38:46 | false | this value |
| Faraday.rb:44:16:44:35 | call to get | This request may run without certificate validation because $@ by $@. | Faraday.rb:43:56:43:58 | arg | the request is disabled | Faraday.rb:47:47:47:71 | VERIFY_NONE | this value |
| HttpClient.rb:6:1:6:33 | call to get | This request may run without certificate validation because $@. | HttpClient.rb:5:33:5:57 | ... = ... | the request is disabled | HttpClient.rb:5:33:5:57 | VERIFY_NONE | this value |
| Httparty.rb:4:1:4:50 | call to get | This request may run without certificate validation because $@. | Httparty.rb:4:45:4:49 | false | the request is disabled | Httparty.rb:4:45:4:49 | false | this value |
| Httparty.rb:7:1:7:55 | call to get | This request may run without certificate validation because $@. | Httparty.rb:7:50:7:54 | false | the request is disabled | Httparty.rb:7:50:7:54 | false | this value |
| Httparty.rb:10:1:10:59 | call to get | This request may run without certificate validation because $@. | Httparty.rb:10:52:10:56 | false | the request is disabled | Httparty.rb:10:52:10:56 | false | this value |
| Httparty.rb:13:1:13:70 | call to post | This request may run without certificate validation because $@. | Httparty.rb:13:65:13:69 | false | the request is disabled | Httparty.rb:13:65:13:69 | false | this value |
| Httparty.rb:16:1:16:74 | call to post | This request may run without certificate validation because $@. | Httparty.rb:16:67:16:71 | false | the request is disabled | Httparty.rb:16:67:16:71 | false | this value |
| NetHttp.rb:9:12:9:31 | call to request | This request may run without certificate validation because $@. | NetHttp.rb:7:20:7:44 | ... = ... | the request is disabled | NetHttp.rb:7:20:7:44 | VERIFY_NONE | this value |
| OpenURI.rb:4:1:4:78 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:4:53:4:77 | VERIFY_NONE | the request is disabled | OpenURI.rb:4:53:4:77 | VERIFY_NONE | this value |
| OpenURI.rb:7:1:7:82 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:7:55:7:79 | VERIFY_NONE | the request is disabled | OpenURI.rb:7:55:7:79 | VERIFY_NONE | this value |
| OpenURI.rb:11:1:11:43 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:10:30:10:54 | VERIFY_NONE | the request is disabled | OpenURI.rb:10:30:10:54 | VERIFY_NONE | this value |
| OpenURI.rb:14:1:14:81 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:14:56:14:80 | VERIFY_NONE | the request is disabled | OpenURI.rb:14:56:14:80 | VERIFY_NONE | this value |
| OpenURI.rb:17:1:17:85 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:17:58:17:82 | VERIFY_NONE | the request is disabled | OpenURI.rb:17:58:17:82 | VERIFY_NONE | this value |
| OpenURI.rb:21:1:21:46 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:20:30:20:54 | VERIFY_NONE | the request is disabled | OpenURI.rb:20:30:20:54 | VERIFY_NONE | this value |
| RestClient.rb:5:12:5:23 | call to get | This request may run without certificate validation because $@. | RestClient.rb:4:72:4:96 | VERIFY_NONE | the request is disabled | RestClient.rb:4:72:4:96 | VERIFY_NONE | this value |
| RestClient.rb:9:12:9:23 | call to get | This request may run without certificate validation because $@. | RestClient.rb:8:74:8:98 | VERIFY_NONE | the request is disabled | RestClient.rb:8:74:8:98 | VERIFY_NONE | this value |
| RestClient.rb:14:12:14:23 | call to get | This request may run without certificate validation because $@. | RestClient.rb:12:25:12:49 | VERIFY_NONE | the request is disabled | RestClient.rb:12:25:12:49 | VERIFY_NONE | this value |
| RestClient.rb:19:12:19:23 | call to get | This request may run without certificate validation because $@ by $@. | RestClient.rb:18:72:18:76 | value | the request is disabled | RestClient.rb:17:9:17:33 | VERIFY_NONE | this value |
| Typhoeus.rb:4:1:4:62 | call to get | This request may run without certificate validation because $@. | Typhoeus.rb:4:57:4:61 | false | the request is disabled | Typhoeus.rb:4:57:4:61 | false | this value |
| Typhoeus.rb:8:1:8:54 | call to post | This request may run without certificate validation because $@. | Typhoeus.rb:7:53:7:57 | false | the request is disabled | Typhoeus.rb:7:53:7:57 | false | this value |
| Excon.rb:6:3:6:34 | call to get | This request may run without certificate validation because $@. | Excon.rb:5:38:5:42 | ... = ... | validation is disabled | Excon.rb:5:38:5:42 | false | this value |
| Excon.rb:12:3:12:34 | call to get | This request may run without certificate validation because $@. | Excon.rb:11:27:11:31 | ... = ... | validation is disabled | Excon.rb:11:27:11:31 | false | this value |
| Excon.rb:18:3:18:34 | call to get | This request may run without certificate validation because $@ by $@. | Excon.rb:17:38:17:60 | ... = ... | validation is disabled | Excon.rb:17:55:17:59 | false | this value |
| Excon.rb:24:3:24:10 | call to get | This request may run without certificate validation because $@. | Excon.rb:23:72:23:76 | false | validation is disabled | Excon.rb:23:72:23:76 | false | this value |
| Excon.rb:30:3:30:62 | call to get | This request may run without certificate validation because $@. | Excon.rb:30:53:30:57 | false | validation is disabled | Excon.rb:30:53:30:57 | false | this value |
| Faraday.rb:5:12:5:30 | call to get | This request may run without certificate validation because $@. | Faraday.rb:4:63:4:67 | false | validation is disabled | Faraday.rb:4:63:4:67 | false | this value |
| Faraday.rb:9:12:9:30 | call to get | This request may run without certificate validation because $@. | Faraday.rb:8:68:8:92 | VERIFY_NONE | validation is disabled | Faraday.rb:8:68:8:92 | VERIFY_NONE | this value |
| Faraday.rb:35:16:35:35 | call to get | This request may run without certificate validation because $@ by $@. | Faraday.rb:34:51:34:53 | arg | validation is disabled | Faraday.rb:38:42:38:46 | false | this value |
| Faraday.rb:44:16:44:35 | call to get | This request may run without certificate validation because $@ by $@. | Faraday.rb:43:56:43:58 | arg | validation is disabled | Faraday.rb:47:47:47:71 | VERIFY_NONE | this value |
| HttpClient.rb:6:1:6:33 | call to get | This request may run without certificate validation because $@. | HttpClient.rb:5:33:5:57 | ... = ... | validation is disabled | HttpClient.rb:5:33:5:57 | VERIFY_NONE | this value |
| Httparty.rb:4:1:4:50 | call to get | This request may run without certificate validation because $@. | Httparty.rb:4:45:4:49 | false | validation is disabled | Httparty.rb:4:45:4:49 | false | this value |
| Httparty.rb:7:1:7:55 | call to get | This request may run without certificate validation because $@. | Httparty.rb:7:50:7:54 | false | validation is disabled | Httparty.rb:7:50:7:54 | false | this value |
| Httparty.rb:10:1:10:59 | call to get | This request may run without certificate validation because $@. | Httparty.rb:10:52:10:56 | false | validation is disabled | Httparty.rb:10:52:10:56 | false | this value |
| Httparty.rb:13:1:13:70 | call to post | This request may run without certificate validation because $@. | Httparty.rb:13:65:13:69 | false | validation is disabled | Httparty.rb:13:65:13:69 | false | this value |
| Httparty.rb:16:1:16:74 | call to post | This request may run without certificate validation because $@. | Httparty.rb:16:67:16:71 | false | validation is disabled | Httparty.rb:16:67:16:71 | false | this value |
| NetHttp.rb:9:12:9:31 | call to request | This request may run without certificate validation because $@. | NetHttp.rb:7:20:7:44 | ... = ... | validation is disabled | NetHttp.rb:7:20:7:44 | VERIFY_NONE | this value |
| OpenURI.rb:4:1:4:78 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:4:53:4:77 | VERIFY_NONE | validation is disabled | OpenURI.rb:4:53:4:77 | VERIFY_NONE | this value |
| OpenURI.rb:7:1:7:82 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:7:55:7:79 | VERIFY_NONE | validation is disabled | OpenURI.rb:7:55:7:79 | VERIFY_NONE | this value |
| OpenURI.rb:11:1:11:43 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:10:30:10:54 | VERIFY_NONE | validation is disabled | OpenURI.rb:10:30:10:54 | VERIFY_NONE | this value |
| OpenURI.rb:14:1:14:81 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:14:56:14:80 | VERIFY_NONE | validation is disabled | OpenURI.rb:14:56:14:80 | VERIFY_NONE | this value |
| OpenURI.rb:17:1:17:85 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:17:58:17:82 | VERIFY_NONE | validation is disabled | OpenURI.rb:17:58:17:82 | VERIFY_NONE | this value |
| OpenURI.rb:21:1:21:46 | call to open | This request may run without certificate validation because $@. | OpenURI.rb:20:30:20:54 | VERIFY_NONE | validation is disabled | OpenURI.rb:20:30:20:54 | VERIFY_NONE | this value |
| RestClient.rb:5:12:5:23 | call to get | This request may run without certificate validation because $@. | RestClient.rb:4:72:4:96 | VERIFY_NONE | validation is disabled | RestClient.rb:4:72:4:96 | VERIFY_NONE | this value |
| RestClient.rb:9:12:9:23 | call to get | This request may run without certificate validation because $@. | RestClient.rb:8:74:8:98 | VERIFY_NONE | validation is disabled | RestClient.rb:8:74:8:98 | VERIFY_NONE | this value |
| RestClient.rb:14:12:14:23 | call to get | This request may run without certificate validation because $@. | RestClient.rb:12:25:12:49 | VERIFY_NONE | validation is disabled | RestClient.rb:12:25:12:49 | VERIFY_NONE | this value |
| RestClient.rb:19:12:19:23 | call to get | This request may run without certificate validation because $@ by $@. | RestClient.rb:18:72:18:76 | value | validation is disabled | RestClient.rb:17:9:17:33 | VERIFY_NONE | this value |
| Typhoeus.rb:4:1:4:62 | call to get | This request may run without certificate validation because $@. | Typhoeus.rb:4:57:4:61 | false | validation is disabled | Typhoeus.rb:4:57:4:61 | false | this value |
| Typhoeus.rb:8:1:8:54 | call to post | This request may run without certificate validation because $@. | Typhoeus.rb:7:53:7:57 | false | validation is disabled | Typhoeus.rb:7:53:7:57 | false | this value |