зеркало из https://github.com/github/codeql.git
Merge branch 'main' into redsun82/swift-action-triggers
This commit is contained in:
Коммит
9fb4ff70b4
|
@ -32,6 +32,14 @@ jobs:
|
|||
- name: Build Swift extractor
|
||||
run: |
|
||||
bazel run //swift:create-extractor-pack
|
||||
- name: Get Swift version
|
||||
id: get_swift_version
|
||||
run: |
|
||||
VERSION=$(bazel run //swift/extractor -- --version | sed -ne 's/.*version \(\S*\).*/\1/p')
|
||||
echo "::set-output name=version::$VERSION"
|
||||
- uses: swift-actions/setup-swift@v1
|
||||
with:
|
||||
swift-version: "${{steps.get_swift_version.outputs.version}}"
|
||||
- name: Run integration tests
|
||||
run: |
|
||||
python integration-tests/runner.py
|
||||
|
|
|
@ -73,10 +73,11 @@
|
|||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
],
|
||||
"DataFlow Java/C# Flow Summaries": [
|
||||
"DataFlow Java/C#/Ruby/Python/Swift Flow Summaries": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
|
||||
],
|
||||
"SsaReadPosition Java/C#": [
|
||||
|
@ -532,7 +533,7 @@
|
|||
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
|
||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
|
||||
"python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/AccessPathSyntax.qll"
|
||||
],
|
||||
"IncompleteUrlSubstringSanitization": [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
|
||||
module ProductFlow {
|
||||
abstract class Configuration extends string {
|
||||
|
@ -11,14 +11,43 @@ module ProductFlow {
|
|||
*
|
||||
* `source1` and `source2` must belong to the same callable.
|
||||
*/
|
||||
abstract predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2);
|
||||
predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `(source1, source2)` is a relevant data flow source with initial states `state1`
|
||||
* and `state2`, respectively.
|
||||
*
|
||||
* `source1` and `source2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSourcePair(
|
||||
DataFlow::Node source1, string state1, DataFlow::Node source2, string state2
|
||||
) {
|
||||
state1 = "" and
|
||||
state2 = "" and
|
||||
this.isSourcePair(source1, source2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(sink1, sink2)` is a relevant data flow sink.
|
||||
*
|
||||
* `sink1` and `sink2` must belong to the same callable.
|
||||
*/
|
||||
abstract predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2);
|
||||
predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `(sink1, sink2)` is a relevant data flow sink with final states `state1`
|
||||
* and `state2`, respectively.
|
||||
*
|
||||
* `sink1` and `sink2` must belong to the same callable.
|
||||
*/
|
||||
predicate isSinkPair(
|
||||
DataFlow::Node sink1, DataFlow::FlowState state1, DataFlow::Node sink2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 = "" and
|
||||
state2 = "" and
|
||||
this.isSinkPair(sink1, sink2)
|
||||
}
|
||||
|
||||
predicate hasFlowPath(
|
||||
DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1,
|
||||
|
@ -34,28 +63,28 @@ module ProductFlow {
|
|||
class Conf1 extends DataFlow::Configuration {
|
||||
Conf1() { this = "Conf1" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Configuration conf | conf.isSourcePair(source, _))
|
||||
override predicate isSource(DataFlow::Node source, string state) {
|
||||
exists(Configuration conf | conf.isSourcePair(source, state, _, _))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(Configuration conf | conf.isSinkPair(sink, _))
|
||||
override predicate isSink(DataFlow::Node sink, string state) {
|
||||
exists(Configuration conf | conf.isSinkPair(sink, state, _, _))
|
||||
}
|
||||
}
|
||||
|
||||
class Conf2 extends DataFlow2::Configuration {
|
||||
Conf2() { this = "Conf2" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
override predicate isSource(DataFlow::Node source, string state) {
|
||||
exists(Configuration conf, DataFlow::Node source1 |
|
||||
conf.isSourcePair(source1, source) and
|
||||
conf.isSourcePair(source1, _, source, state) and
|
||||
any(Conf1 c).hasFlow(source1, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
override predicate isSink(DataFlow::Node sink, string state) {
|
||||
exists(Configuration conf, DataFlow::Node sink1 |
|
||||
conf.isSinkPair(sink1, sink) and any(Conf1 c).hasFlow(_, sink1)
|
||||
conf.isSinkPair(sink1, _, sink, state) and any(Conf1 c).hasFlow(_, sink1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +94,7 @@ module ProductFlow {
|
|||
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
|
||||
DataFlow::PathNode node1, DataFlow2::PathNode node2
|
||||
) {
|
||||
conf.isSourcePair(node1.getNode(), node2.getNode()) and
|
||||
conf.isSourcePair(node1.getNode(), _, node2.getNode(), _) and
|
||||
node1 = source1 and
|
||||
node2 = source2
|
||||
or
|
||||
|
@ -128,7 +157,7 @@ module ProductFlow {
|
|||
) {
|
||||
exists(DataFlow::PathNode mid1, DataFlow2::PathNode mid2 |
|
||||
reachableInterprocEntry(conf, source1, source2, mid1, mid2) and
|
||||
conf.isSinkPair(sink1.getNode(), sink2.getNode()) and
|
||||
conf.isSinkPair(sink1.getNode(), _, sink2.getNode(), _) and
|
||||
localPathStep1*(mid1, sink1) and
|
||||
localPathStep2*(mid2, sink2)
|
||||
)
|
||||
|
|
|
@ -28,6 +28,10 @@ private newtype TBound =
|
|||
i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
|
||||
or
|
||||
i.getAUse() instanceof ArgumentOperand
|
||||
or
|
||||
i instanceof PointerArithmeticInstruction
|
||||
or
|
||||
i.getAUse() instanceof AddressOperand
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -178,11 +178,11 @@ class SemRelationalExpr extends SemBinaryExpr {
|
|||
}
|
||||
|
||||
class SemAddExpr extends SemBinaryExpr {
|
||||
SemAddExpr() { opcode instanceof Opcode::Add }
|
||||
SemAddExpr() { opcode instanceof Opcode::Add or opcode instanceof Opcode::PointerAdd }
|
||||
}
|
||||
|
||||
class SemSubExpr extends SemBinaryExpr {
|
||||
SemSubExpr() { opcode instanceof Opcode::Sub }
|
||||
SemSubExpr() { opcode instanceof Opcode::Sub or opcode instanceof Opcode::PointerSub }
|
||||
}
|
||||
|
||||
class SemMulExpr extends SemBinaryExpr {
|
||||
|
|
|
@ -65,10 +65,18 @@ module Opcode {
|
|||
override string toString() { result = "Add" }
|
||||
}
|
||||
|
||||
class PointerAdd extends Opcode, TPointerAdd {
|
||||
override string toString() { result = "PointerAdd" }
|
||||
}
|
||||
|
||||
class Sub extends Opcode, TSub {
|
||||
override string toString() { result = "Sub" }
|
||||
}
|
||||
|
||||
class PointerSub extends Opcode, TPointerSub {
|
||||
override string toString() { result = "PointerSub" }
|
||||
}
|
||||
|
||||
class Mul extends Opcode, TMul {
|
||||
override string toString() { result = "Mul" }
|
||||
}
|
||||
|
|
|
@ -223,7 +223,9 @@ private SemGuard boundFlowCond(
|
|||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
if getTrackedTypeForSsaVariable(v) instanceof SemIntegerType
|
||||
if
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType or
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemAddressType
|
||||
then
|
||||
upper = true and strengthen = -1
|
||||
or
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
void *malloc(unsigned);
|
||||
unsigned get_size();
|
||||
void write_data(const unsigned char*, const unsigned char*);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
unsigned size = get_size();
|
||||
|
||||
{
|
||||
unsigned char *begin = (unsigned char*)malloc(size);
|
||||
if(!begin) return -1;
|
||||
|
||||
unsigned char* end = begin + size;
|
||||
write_data(begin, end);
|
||||
*end = '\0'; // BAD: Out-of-bounds write
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char *begin = (unsigned char*)malloc(size);
|
||||
if(!begin) return -1;
|
||||
|
||||
unsigned char* end = begin + size;
|
||||
write_data(begin, end);
|
||||
*(end - 1) = '\0'; // GOOD: writing to the last byte
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The program performs an out-of-bounds read or write operation. In addition to causing program instability, techniques exist which may allow an attacker to use this vulnerability to execute arbitrary code.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Ensure that pointer dereferences are properly guarded to ensure that they cannot be used to read or write past the end of the allocation.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The first example allocates a buffer of size <code>size</code> and creates a local variable that stores the location that is one byte past the end of the allocation.
|
||||
This local variable is then dereferenced which results in an out-of-bounds write.
|
||||
The second example subtracts one from the <code>end</code> variable before dereferencing it. This subtraction ensures that the write correctly updates the final byte of the allocation.</p>
|
||||
<sample src="InvalidPointerDeref.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://owasp.org/www-community/vulnerabilities/Buffer_Overflow">Buffer Overflow</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
|
@ -0,0 +1,360 @@
|
|||
/**
|
||||
* @name Invalid pointer dereference
|
||||
* @description Dereferencing a pointer that points past it allocation is undefined behavior
|
||||
* and may lead to security vulnerabilities.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id cpp/invalid-pointer-deref
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-119
|
||||
* external/cwe/cwe-125
|
||||
* external/cwe/cwe-193
|
||||
* external/cwe/cwe-787
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.dataflow.ProductFlow
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysis
|
||||
import experimental.semmle.code.cpp.semantic.SemanticBound
|
||||
import experimental.semmle.code.cpp.semantic.SemanticExprSpecific
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
pragma[nomagic]
|
||||
Instruction getABoundIn(SemBound b, IRFunction func) {
|
||||
result = b.getExpr(0) and
|
||||
result.getEnclosingIRFunction() = func
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i <= b + delta`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate bounded(Instruction i, Instruction b, int delta) {
|
||||
exists(SemBound bound, IRFunction func |
|
||||
semBounded(getSemanticExpr(i), bound, delta, true, _) and
|
||||
b = getABoundIn(bound, func) and
|
||||
i.getEnclosingIRFunction() = func
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the combination of `n` and `state` represents an appropriate
|
||||
* source for the expression `e` suitable for use-use flow.
|
||||
*/
|
||||
private predicate hasSizeImpl(Expr e, DataFlow::Node n, string state) {
|
||||
// The simple case: If the size is a variable access with no qualifier we can just use the
|
||||
// dataflow node for that expression and no state.
|
||||
exists(VariableAccess va |
|
||||
va = e and
|
||||
not va instanceof FieldAccess and
|
||||
n.asConvertedExpr() = va.getFullyConverted() and
|
||||
state = "0"
|
||||
)
|
||||
or
|
||||
// If the size is a choice between two expressions we allow both to be nodes representing the size.
|
||||
exists(ConditionalExpr cond | cond = e | hasSizeImpl([cond.getThen(), cond.getElse()], n, state))
|
||||
or
|
||||
// If the size is an expression plus a constant, we pick the dataflow node of the expression and
|
||||
// remember the constant in the state.
|
||||
exists(Expr const, Expr nonconst |
|
||||
e.(AddExpr).hasOperands(const, nonconst) and
|
||||
state = const.getValue() and
|
||||
hasSizeImpl(nonconst, n, _)
|
||||
)
|
||||
or
|
||||
exists(Expr const, Expr nonconst |
|
||||
e.(SubExpr).hasOperands(const, nonconst) and
|
||||
state = "-" + const.getValue() and
|
||||
hasSizeImpl(nonconst, n, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(n, state)` pair represents the source of flow for the size
|
||||
* expression associated with `alloc`.
|
||||
*/
|
||||
predicate hasSize(AllocationExpr alloc, DataFlow::Node n, string state) {
|
||||
hasSizeImpl(alloc.getSizeExpr(), n, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* A product-flow configuration for flow from an (allocation, size) pair to a
|
||||
* pointer-arithmetic operation that is non-strictly upper-bounded by `allocation + size`.
|
||||
*
|
||||
* The goal of this query is to find patterns such as:
|
||||
* ```cpp
|
||||
* 1. char* begin = (char*)malloc(size);
|
||||
* 2. char* end = begin + size;
|
||||
* 3. for(int *p = begin; p <= end; p++) {
|
||||
* 4. use(*p);
|
||||
* 5. }
|
||||
* ```
|
||||
*
|
||||
* We do this by splitting the task up into two configurations:
|
||||
* 1. `AllocToInvalidPointerConf` find flow from `malloc(size)` to `begin + size`, and
|
||||
* 2. `InvalidPointerToDerefConf` finds flow from `begin + size` to an `end` (on line 3).
|
||||
*
|
||||
* Finally, the range-analysis library will find a load from (or store to) an address that
|
||||
* is non-strictly upper-bounded by `end` (which in this case is `*p`).
|
||||
*/
|
||||
class AllocToInvalidPointerConf extends ProductFlow::Configuration {
|
||||
AllocToInvalidPointerConf() { this = "AllocToInvalidPointerConf" }
|
||||
|
||||
override predicate isSourcePair(
|
||||
DataFlow::Node source1, string state1, DataFlow::Node source2, string state2
|
||||
) {
|
||||
// In the case of an allocation like
|
||||
// ```cpp
|
||||
// malloc(size + 1);
|
||||
// ```
|
||||
// we use `state2` to remember that there was an offset (in this case an offset of `1`) added
|
||||
// to the size of the allocation. This state is then checked in `isSinkPair`.
|
||||
state1 = "" and
|
||||
hasSize(source1.asConvertedExpr(), source2, state2)
|
||||
}
|
||||
|
||||
override predicate isSinkPair(
|
||||
DataFlow::Node sink1, DataFlow::FlowState state1, DataFlow::Node sink2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 = "" and
|
||||
// We check that the delta computed by the range analysis matches the
|
||||
// state value that we set in `isSourcePair`.
|
||||
exists(int delta |
|
||||
isSinkImpl(_, sink1, sink2, delta) and
|
||||
state2 = delta.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate pointerAddInstructionHasOperands(
|
||||
PointerAddInstruction pai, Instruction left, Instruction right
|
||||
) {
|
||||
pai.getLeft() = left and
|
||||
pai.getRight() = right
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pai` is non-strictly upper bounded by `sink2 + delta` and `sink1` is the
|
||||
* left operand of the pointer-arithmetic operation.
|
||||
*
|
||||
* For example in,
|
||||
* ```cpp
|
||||
* char* end = p + (size + 1);
|
||||
* ```
|
||||
* We will have:
|
||||
* - `pai` is `p + (size + 1)`,
|
||||
* - `sink1` is `p`
|
||||
* - `sink2` is `size`
|
||||
* - `delta` is `1`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate pointerAddInstructionHasBounds(
|
||||
PointerAddInstruction pai, DataFlow::Node sink1, Instruction sink2, int delta
|
||||
) {
|
||||
exists(Instruction right |
|
||||
pointerAddInstructionHasOperands(pai, sink1.asInstruction(), right) and
|
||||
bounded(right, sink2, delta)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pai` is non-strictly upper bounded by `sink2 + delta` and `sink1` is the
|
||||
* left operand of the pointer-arithmetic operation.
|
||||
*
|
||||
* See `pointerAddInstructionHasBounds` for an example.
|
||||
*/
|
||||
predicate isSinkImpl(
|
||||
PointerAddInstruction pai, DataFlow::Node sink1, DataFlow::Node sink2, int delta
|
||||
) {
|
||||
pointerAddInstructionHasBounds(pai, sink1, sink2.asInstruction(), delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a sink for `InvalidPointerToDerefConf` and `i` is a `StoreInstruction` that
|
||||
* writes to an address that non-strictly upper-bounds `sink`, or `i` is a `LoadInstruction` that
|
||||
* reads from an address that non-strictly upper-bounds `sink`.
|
||||
*/
|
||||
predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string operation) {
|
||||
exists(AddressOperand addr, int delta |
|
||||
bounded(addr.getDef(), sink.asInstruction(), delta) and
|
||||
delta >= 0 and
|
||||
i.getAnOperand() = addr
|
||||
|
|
||||
i instanceof StoreInstruction and
|
||||
operation = "write"
|
||||
or
|
||||
i instanceof LoadInstruction and
|
||||
operation = "read"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to track flow from a pointer-arithmetic operation found
|
||||
* by `AllocToInvalidPointerConf` to a dereference of the pointer.
|
||||
*/
|
||||
class InvalidPointerToDerefConf extends DataFlow3::Configuration {
|
||||
InvalidPointerToDerefConf() { this = "InvalidPointerToDerefConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pai` is a pointer-arithmetic operation and `source` is a dataflow node with a
|
||||
* pointer-value that is non-strictly upper bounded by `pai + delta`.
|
||||
*
|
||||
* For example, if `pai` is a pointer-arithmetic operation `p + size` in an expression such
|
||||
* as `(p + size) + 1` and `source` is the node representing `(p + size) + 1`. In this
|
||||
* case `delta` is 1.
|
||||
*/
|
||||
predicate invalidPointerToDerefSource(
|
||||
PointerArithmeticInstruction pai, DataFlow::Node source, int delta
|
||||
) {
|
||||
exists(ProductFlow::Configuration conf, DataFlow::PathNode p, DataFlow::Node sink1 |
|
||||
p.getNode() = sink1 and
|
||||
conf.hasFlowPath(_, _, p, _) and
|
||||
isSinkImpl(pai, sink1, _, _) and
|
||||
bounded(source.asInstruction(), pai, delta) and
|
||||
delta >= 0
|
||||
)
|
||||
}
|
||||
|
||||
newtype TMergedPathNode =
|
||||
// The path nodes computed by the first projection of `AllocToInvalidPointerConf`
|
||||
TPathNode1(DataFlow::PathNode p) or
|
||||
// The path nodes computed by `InvalidPointerToDerefConf`
|
||||
TPathNode3(DataFlow3::PathNode p) or
|
||||
// The read/write that uses the invalid pointer identified by `InvalidPointerToDerefConf`.
|
||||
// This one is needed because the sink identified by `InvalidPointerToDerefConf` is the
|
||||
// pointer, but we want to raise an alert at the dereference.
|
||||
TPathNodeSink(Instruction i) {
|
||||
exists(DataFlow::Node n |
|
||||
any(InvalidPointerToDerefConf conf).hasFlow(_, n) and
|
||||
isInvalidPointerDerefSink(n, i, _)
|
||||
)
|
||||
}
|
||||
|
||||
class MergedPathNode extends TMergedPathNode {
|
||||
string toString() { none() }
|
||||
|
||||
final DataFlow::PathNode asPathNode1() { this = TPathNode1(result) }
|
||||
|
||||
final DataFlow3::PathNode asPathNode3() { this = TPathNode3(result) }
|
||||
|
||||
final Instruction asSinkNode() { this = TPathNodeSink(result) }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
class PathNode1 extends MergedPathNode, TPathNode1 {
|
||||
override string toString() {
|
||||
exists(DataFlow::PathNode p |
|
||||
this = TPathNode1(p) and
|
||||
result = p.toString()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
class PathNode3 extends MergedPathNode, TPathNode3 {
|
||||
override string toString() {
|
||||
exists(DataFlow3::PathNode p |
|
||||
this = TPathNode3(p) and
|
||||
result = p.toString()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asPathNode3().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
class PathSinkNode extends MergedPathNode, TPathNodeSink {
|
||||
override string toString() {
|
||||
exists(Instruction i |
|
||||
this = TPathNodeSink(i) and
|
||||
result = i.toString()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asSinkNode()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate edges(MergedPathNode node1, MergedPathNode node2) {
|
||||
node1.asPathNode1().getASuccessor() = node2.asPathNode1()
|
||||
or
|
||||
joinOn1(_, node1.asPathNode1(), node2.asPathNode3())
|
||||
or
|
||||
node1.asPathNode3().getASuccessor() = node2.asPathNode3()
|
||||
or
|
||||
joinOn2(node1.asPathNode3(), node2.asSinkNode(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p1` is a sink of `AllocToInvalidPointerConf` and `p2` is a source
|
||||
* of `InvalidPointerToDerefConf`, and they are connected through `pai`.
|
||||
*/
|
||||
predicate joinOn1(PointerArithmeticInstruction pai, DataFlow::PathNode p1, DataFlow3::PathNode p2) {
|
||||
isSinkImpl(pai, p1.getNode(), _, _) and
|
||||
invalidPointerToDerefSource(pai, p2.getNode(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p1` is a sink of `InvalidPointerToDerefConf` and `i` is the instruction
|
||||
* that dereferences `p1`. The string `operation` describes whether the `i` is
|
||||
* a `StoreInstruction` or `LoadInstruction`.
|
||||
*/
|
||||
predicate joinOn2(DataFlow3::PathNode p1, Instruction i, string operation) {
|
||||
isInvalidPointerDerefSink(p1.getNode(), i, operation)
|
||||
}
|
||||
|
||||
predicate hasFlowPath(
|
||||
MergedPathNode source1, MergedPathNode sink, DataFlow3::PathNode source3,
|
||||
PointerArithmeticInstruction pai, string operation
|
||||
) {
|
||||
exists(
|
||||
AllocToInvalidPointerConf conf1, InvalidPointerToDerefConf conf2, DataFlow3::PathNode sink3,
|
||||
DataFlow::PathNode sink1
|
||||
|
|
||||
conf1.hasFlowPath(source1.asPathNode1(), _, sink1, _) and
|
||||
joinOn1(pai, sink1, source3) and
|
||||
conf2.hasFlowPath(source3, sink3) and
|
||||
joinOn2(sink3, sink.asSinkNode(), operation)
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
MergedPathNode source, MergedPathNode sink, int k, string kstr, DataFlow3::PathNode source3,
|
||||
PointerArithmeticInstruction pai, string operation, Expr offset, DataFlow::Node n
|
||||
where
|
||||
hasFlowPath(source, sink, source3, pai, operation) and
|
||||
invalidPointerToDerefSource(pai, source3.getNode(), k) and
|
||||
offset = pai.getRight().getUnconvertedResultExpression() and
|
||||
n = source.asPathNode1().getNode() and
|
||||
if k = 0 then kstr = "" else kstr = " + " + k
|
||||
select sink, source, sink,
|
||||
"This " + operation + " might be out of bounds, as the pointer might be equal to $@ + $@" + kstr +
|
||||
".", n, n.toString(), offset, offset.toString()
|
|
@ -20,10 +20,12 @@
|
|||
| test.cpp:62:10:62:13 | Load: iter | test.cpp:60:17:60:17 | ValueNumberBound | 0 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
|
||||
| test.cpp:62:10:62:13 | Load: iter | test.cpp:60:17:60:17 | ValueNumberBound | 3 | true | CompareLT: ... < ... | test.cpp:61:32:61:51 | test.cpp:61:32:61:51 |
|
||||
| test.cpp:62:10:62:13 | Load: iter | test.cpp:61:39:61:51 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:61:32:61:51 | test.cpp:61:32:61:51 |
|
||||
| test.cpp:62:10:62:13 | Load: iter | test.cpp:61:48:61:50 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:61:32:61:51 | test.cpp:61:32:61:51 |
|
||||
| test.cpp:67:10:67:13 | Load: iter | test.cpp:60:17:60:17 | ValueNumberBound | 0 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
|
||||
| test.cpp:67:10:67:13 | Load: iter | test.cpp:60:17:60:17 | ValueNumberBound | 3 | true | CompareLT: ... < ... | test.cpp:66:32:66:41 | test.cpp:66:32:66:41 |
|
||||
| test.cpp:67:10:67:13 | Load: iter | test.cpp:61:32:61:35 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:66:32:66:41 | test.cpp:66:32:66:41 |
|
||||
| test.cpp:67:10:67:13 | Load: iter | test.cpp:61:39:61:51 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:66:32:66:41 | test.cpp:66:32:66:41 |
|
||||
| test.cpp:67:10:67:13 | Load: iter | test.cpp:61:48:61:50 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:66:32:66:41 | test.cpp:66:32:66:41 |
|
||||
| test.cpp:77:12:77:12 | Load: i | file://:0:0:0:0 | 0 | 0 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
|
||||
| test.cpp:77:12:77:12 | Load: i | test.cpp:72:15:72:15 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:76:20:76:24 | test.cpp:76:20:76:24 |
|
||||
| test.cpp:77:12:77:12 | Load: i | test.cpp:72:22:72:22 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:76:20:76:24 | test.cpp:76:20:76:24 |
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
| test.cpp:19:19:19:24 | call to malloc | test.cpp:18:17:18:20 | size | test.cpp:26:18:26:23 | string | test.cpp:26:31:26:39 | (size_t)... |
|
||||
| test.cpp:19:19:19:24 | call to malloc | test.cpp:18:17:18:20 | size | test.cpp:30:18:30:23 | string | test.cpp:30:31:30:39 | (size_t)... |
|
||||
| test.cpp:19:19:19:24 | call to malloc | test.cpp:18:17:18:20 | size | test.cpp:26:18:26:23 | Load | test.cpp:26:31:26:39 | Convert |
|
||||
| test.cpp:19:19:19:24 | call to malloc | test.cpp:18:17:18:20 | size | test.cpp:30:18:30:23 | Load | test.cpp:30:31:30:39 | Convert |
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:24:4:27 | size | test.cpp:10:9:10:11 | arr | test.cpp:4:24:4:27 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:24:4:27 | size | test.cpp:10:9:10:11 | arr | test.cpp:4:24:4:27 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:24:4:27 | size | test.cpp:10:9:10:11 | arr | test.cpp:5:25:5:28 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:24:4:27 | size | test.cpp:10:9:10:11 | arr | test.cpp:5:25:5:28 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:24:4:27 | size | test.cpp:10:9:10:11 | arr | test.cpp:9:26:9:29 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:5:25:5:28 | size | test.cpp:10:9:10:11 | arr | test.cpp:5:25:5:28 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:5:25:5:28 | size | test.cpp:10:9:10:11 | arr | test.cpp:5:25:5:28 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:5:25:5:28 | size | test.cpp:10:9:10:11 | arr | test.cpp:9:26:9:29 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:9:26:9:29 | size | test.cpp:10:9:10:11 | arr | test.cpp:9:26:9:29 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:9:26:9:29 | size | test.cpp:10:9:10:11 | arr | test.cpp:9:26:9:29 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | p | test.cpp:55:5:55:19 | Store |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | p | test.cpp:55:16:55:19 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | p | test.cpp:55:16:55:19 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | p | test.cpp:56:20:56:23 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:56:20:56:23 | size | test.cpp:63:13:63:13 | p | test.cpp:56:20:56:23 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:56:20:56:23 | size | test.cpp:63:13:63:13 | p | test.cpp:56:20:56:23 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:58:29:58:32 | size | test.cpp:63:13:63:13 | p | test.cpp:58:29:58:32 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:58:29:58:32 | size | test.cpp:63:13:63:13 | p | test.cpp:58:29:58:32 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:62:30:62:33 | size | test.cpp:63:13:63:13 | p | test.cpp:62:30:62:33 | size |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:62:30:62:33 | size | test.cpp:63:13:63:13 | p | test.cpp:62:30:62:33 | size |
|
||||
| test.cpp:70:14:70:19 | call to malloc | test.cpp:69:17:69:20 | size | test.cpp:83:14:83:14 | p | test.cpp:82:31:82:34 | size |
|
||||
| test.cpp:70:14:70:19 | call to malloc | test.cpp:69:17:69:20 | size | test.cpp:93:14:93:14 | p | test.cpp:88:30:88:33 | size |
|
||||
| test.cpp:70:14:70:19 | call to malloc | test.cpp:69:17:69:20 | size | test.cpp:93:14:93:14 | p | test.cpp:92:31:92:34 | size |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:24:4:27 | size | test.cpp:10:9:10:11 | Load | test.cpp:5:25:5:28 | Load |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:4:24:4:27 | size | test.cpp:10:9:10:11 | Load | test.cpp:9:26:9:29 | Load |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:5:25:5:28 | size | test.cpp:10:9:10:11 | Load | test.cpp:5:25:5:28 | Load |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:5:25:5:28 | size | test.cpp:10:9:10:11 | Load | test.cpp:9:26:9:29 | Load |
|
||||
| test.cpp:4:17:4:22 | call to malloc | test.cpp:9:26:9:29 | size | test.cpp:10:9:10:11 | Load | test.cpp:9:26:9:29 | Load |
|
||||
| test.cpp:22:13:22:18 | call to malloc | test.cpp:21:16:21:19 | size | test.cpp:35:13:35:13 | Load | test.cpp:30:29:30:32 | Load |
|
||||
| test.cpp:22:13:22:18 | call to malloc | test.cpp:21:16:21:19 | size | test.cpp:35:13:35:13 | Load | test.cpp:34:30:34:33 | Load |
|
||||
| test.cpp:22:13:22:18 | call to malloc | test.cpp:21:16:21:19 | size | test.cpp:45:13:45:13 | Load | test.cpp:40:29:40:32 | Load |
|
||||
| test.cpp:22:13:22:18 | call to malloc | test.cpp:21:16:21:19 | size | test.cpp:45:13:45:13 | Load | test.cpp:44:30:44:33 | Load |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | Load | test.cpp:55:5:55:19 | Store |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | Load | test.cpp:55:5:55:19 | Store |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | Load | test.cpp:55:16:55:19 | Load |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | Load | test.cpp:56:20:56:23 | Load |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | Load | test.cpp:58:29:58:32 | Load |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:55:16:55:19 | size | test.cpp:63:13:63:13 | Load | test.cpp:62:30:62:33 | Load |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:58:29:58:32 | size | test.cpp:63:13:63:13 | Load | test.cpp:58:29:58:32 | Load |
|
||||
| test.cpp:56:13:56:18 | call to malloc | test.cpp:62:30:62:33 | size | test.cpp:63:13:63:13 | Load | test.cpp:62:30:62:33 | Load |
|
||||
| test.cpp:70:14:70:19 | call to malloc | test.cpp:69:17:69:20 | size | test.cpp:83:14:83:14 | Load | test.cpp:82:31:82:34 | Load |
|
||||
| test.cpp:70:14:70:19 | call to malloc | test.cpp:69:17:69:20 | size | test.cpp:93:14:93:14 | Load | test.cpp:88:30:88:33 | Load |
|
||||
| test.cpp:70:14:70:19 | call to malloc | test.cpp:69:17:69:20 | size | test.cpp:93:14:93:14 | Load | test.cpp:92:31:92:34 | Load |
|
||||
|
|
|
@ -32,7 +32,7 @@ void test2(int size) {
|
|||
}
|
||||
|
||||
for (int i = 0; i <= arr.size; i++) {
|
||||
arr.p[i] = i; // BAD [NOT DETECTED]
|
||||
arr.p[i] = i; // BAD
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ void test3_callee(array_t arr) {
|
|||
}
|
||||
|
||||
for (int i = 0; i <= arr.size; i++) {
|
||||
arr.p[i] = i; // BAD [NOT DETECTED]
|
||||
arr.p[i] = i; // BAD
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,627 @@
|
|||
edges
|
||||
| test.cpp:4:15:4:20 | call to malloc | test.cpp:5:15:5:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:22 | Store | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:6:15:6:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:7:16:7:16 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:8:16:8:20 | ... + ... | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:8:16:8:20 | ... + ... | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:10:16:10:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:10:16:10:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:10:16:10:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:10:16:10:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:10:16:10:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:10:16:10:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:10:16:10:16 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:11:16:11:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:11:16:11:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:11:16:11:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:11:16:11:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:11:16:11:16 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:16:15:16:20 | call to malloc | test.cpp:17:15:17:15 | Load |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:22 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:17:15:17:22 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:20:16:20:20 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:20:16:20:20 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:28:15:28:20 | call to malloc | test.cpp:29:15:29:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:28 | Store | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:30:15:30:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:31:16:31:16 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:32:16:32:20 | ... + ... | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:32:16:32:20 | ... + ... | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:34:16:34:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:34:16:34:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:34:16:34:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:34:16:34:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:34:16:34:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:34:16:34:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:34:16:34:16 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:35:16:35:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:35:16:35:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:35:16:35:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:35:16:35:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:35:16:35:16 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:40:15:40:20 | call to malloc | test.cpp:41:15:41:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:28 | Store | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:42:15:42:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:43:16:43:16 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:44:16:44:20 | ... + ... | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:44:16:44:20 | ... + ... | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:46:16:46:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:46:16:46:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:46:16:46:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:46:16:46:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:46:16:46:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:46:16:46:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:46:16:46:16 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:47:16:47:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:47:16:47:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:47:16:47:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:47:16:47:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:47:16:47:16 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:51:7:51:14 | VariableAddress indirection | test.cpp:62:39:62:39 | Load |
|
||||
| test.cpp:51:7:51:14 | VariableAddress indirection | test.cpp:66:39:66:39 | Load |
|
||||
| test.cpp:51:7:51:14 | VariableAddress indirection | test.cpp:70:38:70:38 | Load |
|
||||
| test.cpp:51:33:51:35 | Load indirection | test.cpp:60:34:60:37 | mk_array output argument |
|
||||
| test.cpp:52:19:52:24 | call to malloc | test.cpp:51:7:51:14 | VariableAddress indirection |
|
||||
| test.cpp:52:19:52:24 | call to malloc | test.cpp:53:12:53:16 | Load |
|
||||
| test.cpp:53:5:53:23 | Store | test.cpp:51:33:51:35 | Load indirection |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:5:53:23 | Store |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:5:53:23 | Store |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:12:53:23 | ... + ... |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:12:53:23 | ... + ... |
|
||||
| test.cpp:53:12:53:23 | ... + ... | test.cpp:51:33:51:35 | Load indirection |
|
||||
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:62:32:62:34 | Load |
|
||||
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:66:32:66:34 | Load |
|
||||
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:70:31:70:33 | Load |
|
||||
| test.cpp:62:32:62:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:62:32:62:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:66:32:66:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:66:32:66:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:70:31:70:33 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:70:31:70:33 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [begin] | test.cpp:91:20:91:22 | arr indirection [begin] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [begin] | test.cpp:95:20:95:22 | arr indirection [begin] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [begin] | test.cpp:99:20:99:22 | arr indirection [begin] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [begin] | test.cpp:119:18:119:25 | call to mk_array [begin] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [end] | test.cpp:91:36:91:38 | arr indirection [end] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [end] | test.cpp:95:36:95:38 | arr indirection [end] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [end] | test.cpp:99:35:99:37 | arr indirection [end] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [end] | test.cpp:119:18:119:25 | call to mk_array [end] |
|
||||
| test.cpp:82:5:82:28 | Store | test.cpp:82:9:82:13 | arr indirection [post update] [begin] |
|
||||
| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:80:9:80:16 | VariableAddress indirection [begin] |
|
||||
| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:83:15:83:17 | arr indirection [begin] |
|
||||
| test.cpp:82:17:82:22 | call to malloc | test.cpp:82:5:82:28 | Store |
|
||||
| test.cpp:83:5:83:30 | Store | test.cpp:83:9:83:11 | arr indirection [post update] [end] |
|
||||
| test.cpp:83:9:83:11 | arr indirection [post update] [end] | test.cpp:80:9:80:16 | VariableAddress indirection [end] |
|
||||
| test.cpp:83:15:83:17 | arr indirection [begin] | test.cpp:83:19:83:23 | begin |
|
||||
| test.cpp:83:15:83:30 | ... + ... | test.cpp:83:5:83:30 | Store |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:5:83:30 | Store |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:5:83:30 | Store |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:15:83:30 | ... + ... |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:15:83:30 | ... + ... |
|
||||
| test.cpp:83:19:83:23 | begin | test.cpp:83:19:83:23 | Load |
|
||||
| test.cpp:91:20:91:22 | arr indirection [begin] | test.cpp:91:24:91:28 | begin |
|
||||
| test.cpp:91:20:91:22 | arr indirection [begin] | test.cpp:91:47:91:47 | Load |
|
||||
| test.cpp:91:24:91:28 | begin | test.cpp:91:47:91:47 | Load |
|
||||
| test.cpp:91:36:91:38 | arr indirection [end] | test.cpp:91:40:91:42 | end |
|
||||
| test.cpp:91:40:91:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:91:40:91:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:91:40:91:42 | end | test.cpp:91:40:91:42 | Load |
|
||||
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:24:95:28 | begin |
|
||||
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:47:95:47 | Load |
|
||||
| test.cpp:95:24:95:28 | begin | test.cpp:95:47:95:47 | Load |
|
||||
| test.cpp:95:36:95:38 | arr indirection [end] | test.cpp:95:40:95:42 | end |
|
||||
| test.cpp:95:40:95:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:95:40:95:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:95:40:95:42 | end | test.cpp:95:40:95:42 | Load |
|
||||
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:24:99:28 | begin |
|
||||
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:46:99:46 | Load |
|
||||
| test.cpp:99:24:99:28 | begin | test.cpp:99:46:99:46 | Load |
|
||||
| test.cpp:99:35:99:37 | arr indirection [end] | test.cpp:99:39:99:41 | end |
|
||||
| test.cpp:99:39:99:41 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:99:39:99:41 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:99:39:99:41 | end | test.cpp:99:39:99:41 | Load |
|
||||
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:105:20:105:22 | arr indirection [begin] |
|
||||
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:109:20:109:22 | arr indirection [begin] |
|
||||
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:113:20:113:22 | arr indirection [begin] |
|
||||
| test.cpp:104:27:104:29 | arr [end] | test.cpp:105:36:105:38 | arr indirection [end] |
|
||||
| test.cpp:104:27:104:29 | arr [end] | test.cpp:109:36:109:38 | arr indirection [end] |
|
||||
| test.cpp:104:27:104:29 | arr [end] | test.cpp:113:35:113:37 | arr indirection [end] |
|
||||
| test.cpp:105:20:105:22 | arr indirection [begin] | test.cpp:105:24:105:28 | begin |
|
||||
| test.cpp:105:20:105:22 | arr indirection [begin] | test.cpp:105:47:105:47 | Load |
|
||||
| test.cpp:105:24:105:28 | begin | test.cpp:105:47:105:47 | Load |
|
||||
| test.cpp:105:36:105:38 | arr indirection [end] | test.cpp:105:40:105:42 | end |
|
||||
| test.cpp:105:40:105:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:105:40:105:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:105:40:105:42 | end | test.cpp:105:40:105:42 | Load |
|
||||
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:24:109:28 | begin |
|
||||
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:47:109:47 | Load |
|
||||
| test.cpp:109:24:109:28 | begin | test.cpp:109:47:109:47 | Load |
|
||||
| test.cpp:109:36:109:38 | arr indirection [end] | test.cpp:109:40:109:42 | end |
|
||||
| test.cpp:109:40:109:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:109:40:109:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:109:40:109:42 | end | test.cpp:109:40:109:42 | Load |
|
||||
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:24:113:28 | begin |
|
||||
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:46:113:46 | Load |
|
||||
| test.cpp:113:24:113:28 | begin | test.cpp:113:46:113:46 | Load |
|
||||
| test.cpp:113:35:113:37 | arr indirection [end] | test.cpp:113:39:113:41 | end |
|
||||
| test.cpp:113:39:113:41 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:113:39:113:41 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:113:39:113:41 | end | test.cpp:113:39:113:41 | Load |
|
||||
| test.cpp:119:18:119:25 | call to mk_array [begin] | test.cpp:104:27:104:29 | arr [begin] |
|
||||
| test.cpp:119:18:119:25 | call to mk_array [end] | test.cpp:104:27:104:29 | arr [end] |
|
||||
| test.cpp:124:15:124:20 | call to malloc | test.cpp:125:5:125:17 | Store |
|
||||
| test.cpp:124:15:124:20 | call to malloc | test.cpp:126:15:126:15 | Load |
|
||||
| test.cpp:125:5:125:17 | Store | test.cpp:125:9:125:13 | arr indirection [post update] [begin] |
|
||||
| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:129:11:129:13 | arr indirection [begin] |
|
||||
| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:133:11:133:13 | arr indirection [begin] |
|
||||
| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:137:11:137:13 | arr indirection [begin] |
|
||||
| test.cpp:129:11:129:13 | arr indirection [begin] | test.cpp:129:15:129:19 | begin |
|
||||
| test.cpp:129:15:129:19 | begin | test.cpp:129:15:129:19 | Load |
|
||||
| test.cpp:133:11:133:13 | arr indirection [begin] | test.cpp:133:15:133:19 | begin |
|
||||
| test.cpp:133:15:133:19 | begin | test.cpp:133:15:133:19 | Load |
|
||||
| test.cpp:137:11:137:13 | arr indirection [begin] | test.cpp:137:15:137:19 | begin |
|
||||
| test.cpp:137:15:137:19 | begin | test.cpp:137:15:137:19 | Load |
|
||||
| test.cpp:141:10:141:19 | VariableAddress indirection [begin] | test.cpp:150:20:150:29 | Call indirection [begin] |
|
||||
| test.cpp:141:10:141:19 | VariableAddress indirection [begin] | test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] |
|
||||
| test.cpp:141:10:141:19 | VariableAddress indirection [end] | test.cpp:150:20:150:29 | Call indirection [end] |
|
||||
| test.cpp:141:10:141:19 | VariableAddress indirection [end] | test.cpp:180:19:180:28 | call to mk_array_p indirection [end] |
|
||||
| test.cpp:143:5:143:29 | Store | test.cpp:143:10:143:14 | Load indirection [post update] [begin] |
|
||||
| test.cpp:143:10:143:14 | Load indirection [post update] [begin] | test.cpp:141:10:141:19 | VariableAddress indirection [begin] |
|
||||
| test.cpp:143:10:143:14 | Load indirection [post update] [begin] | test.cpp:144:16:144:18 | Load indirection [begin] |
|
||||
| test.cpp:143:18:143:23 | call to malloc | test.cpp:143:5:143:29 | Store |
|
||||
| test.cpp:144:5:144:32 | Store | test.cpp:144:10:144:12 | Load indirection [post update] [end] |
|
||||
| test.cpp:144:10:144:12 | Load indirection [post update] [end] | test.cpp:141:10:141:19 | VariableAddress indirection [end] |
|
||||
| test.cpp:144:16:144:18 | Load indirection [begin] | test.cpp:144:21:144:25 | begin |
|
||||
| test.cpp:144:16:144:32 | ... + ... | test.cpp:144:5:144:32 | Store |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:5:144:32 | Store |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:5:144:32 | Store |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:16:144:32 | ... + ... |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:16:144:32 | ... + ... |
|
||||
| test.cpp:144:21:144:25 | begin | test.cpp:144:21:144:25 | Load |
|
||||
| test.cpp:150:20:150:29 | Call indirection [begin] | test.cpp:152:20:152:22 | Load indirection [begin] |
|
||||
| test.cpp:150:20:150:29 | Call indirection [begin] | test.cpp:156:20:156:22 | Load indirection [begin] |
|
||||
| test.cpp:150:20:150:29 | Call indirection [begin] | test.cpp:160:20:160:22 | Load indirection [begin] |
|
||||
| test.cpp:150:20:150:29 | Call indirection [end] | test.cpp:156:37:156:39 | Load indirection [end] |
|
||||
| test.cpp:152:20:152:22 | Load indirection [begin] | test.cpp:152:25:152:29 | begin |
|
||||
| test.cpp:152:20:152:22 | Load indirection [begin] | test.cpp:152:49:152:49 | Load |
|
||||
| test.cpp:152:25:152:29 | begin | test.cpp:152:49:152:49 | Load |
|
||||
| test.cpp:156:20:156:22 | Load indirection [begin] | test.cpp:156:25:156:29 | begin |
|
||||
| test.cpp:156:20:156:22 | Load indirection [begin] | test.cpp:156:49:156:49 | Load |
|
||||
| test.cpp:156:25:156:29 | begin | test.cpp:156:49:156:49 | Load |
|
||||
| test.cpp:156:37:156:39 | Load indirection [end] | test.cpp:156:42:156:44 | end |
|
||||
| test.cpp:156:42:156:44 | Load | test.cpp:157:9:157:14 | Store: ... = ... |
|
||||
| test.cpp:156:42:156:44 | Load | test.cpp:157:9:157:14 | Store: ... = ... |
|
||||
| test.cpp:156:42:156:44 | end | test.cpp:156:42:156:44 | Load |
|
||||
| test.cpp:160:20:160:22 | Load indirection [begin] | test.cpp:160:25:160:29 | begin |
|
||||
| test.cpp:160:20:160:22 | Load indirection [begin] | test.cpp:160:48:160:48 | Load |
|
||||
| test.cpp:160:25:160:29 | begin | test.cpp:160:48:160:48 | Load |
|
||||
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:166:20:166:22 | Load indirection [begin] |
|
||||
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:170:20:170:22 | Load indirection [begin] |
|
||||
| test.cpp:165:29:165:31 | arr indirection [begin] | test.cpp:174:20:174:22 | Load indirection [begin] |
|
||||
| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:166:37:166:39 | Load indirection [end] |
|
||||
| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:170:37:170:39 | Load indirection [end] |
|
||||
| test.cpp:165:29:165:31 | arr indirection [end] | test.cpp:174:36:174:38 | Load indirection [end] |
|
||||
| test.cpp:166:20:166:22 | Load indirection [begin] | test.cpp:166:25:166:29 | begin |
|
||||
| test.cpp:166:20:166:22 | Load indirection [begin] | test.cpp:166:49:166:49 | Load |
|
||||
| test.cpp:166:25:166:29 | begin | test.cpp:166:49:166:49 | Load |
|
||||
| test.cpp:166:37:166:39 | Load indirection [end] | test.cpp:166:42:166:44 | end |
|
||||
| test.cpp:166:42:166:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:166:42:166:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:166:42:166:44 | end | test.cpp:166:42:166:44 | Load |
|
||||
| test.cpp:170:20:170:22 | Load indirection [begin] | test.cpp:170:25:170:29 | begin |
|
||||
| test.cpp:170:20:170:22 | Load indirection [begin] | test.cpp:170:49:170:49 | Load |
|
||||
| test.cpp:170:25:170:29 | begin | test.cpp:170:49:170:49 | Load |
|
||||
| test.cpp:170:37:170:39 | Load indirection [end] | test.cpp:170:42:170:44 | end |
|
||||
| test.cpp:170:42:170:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:170:42:170:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:170:42:170:44 | end | test.cpp:170:42:170:44 | Load |
|
||||
| test.cpp:174:20:174:22 | Load indirection [begin] | test.cpp:174:25:174:29 | begin |
|
||||
| test.cpp:174:20:174:22 | Load indirection [begin] | test.cpp:174:48:174:48 | Load |
|
||||
| test.cpp:174:25:174:29 | begin | test.cpp:174:48:174:48 | Load |
|
||||
| test.cpp:174:36:174:38 | Load indirection [end] | test.cpp:174:41:174:43 | end |
|
||||
| test.cpp:174:41:174:43 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:174:41:174:43 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:174:41:174:43 | end | test.cpp:174:41:174:43 | Load |
|
||||
| test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] | test.cpp:165:29:165:31 | arr indirection [begin] |
|
||||
| test.cpp:180:19:180:28 | call to mk_array_p indirection [end] | test.cpp:165:29:165:31 | arr indirection [end] |
|
||||
| test.cpp:188:15:188:20 | call to malloc | test.cpp:189:15:189:15 | Load |
|
||||
#select
|
||||
| test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | 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 |
|
||||
| test.cpp:8:14:8:21 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:8:14:8:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
|
||||
| test.cpp:8:14:8:21 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:8:14:8:21 | Load: * ... | 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 |
|
||||
| test.cpp:20:14:20:21 | Load: * ... | test.cpp:16:15:16:20 | call to malloc | test.cpp:20:14:20:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:16:15:16:20 | call to malloc | call to malloc | test.cpp:17:19:17:22 | size | size |
|
||||
| test.cpp:30:14:30:15 | Load: * ... | test.cpp:28:15:28:20 | call to malloc | test.cpp:30:14:30:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:28:15:28:20 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
|
||||
| test.cpp:32:14:32:21 | Load: * ... | test.cpp:28:15:28:20 | call to malloc | test.cpp:32:14:32:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:28:15:28:20 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
|
||||
| test.cpp:32:14:32:21 | Load: * ... | test.cpp:28:15:28:20 | call to malloc | test.cpp:32:14:32:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:28:15:28:20 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
|
||||
| test.cpp:42:14:42:15 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:42:14:42:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
|
||||
| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
|
||||
| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
|
||||
| test.cpp:67:9:67:14 | Store: ... = ... | test.cpp:52:19:52:24 | call to malloc | test.cpp:67:9:67:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:52:19:52:24 | call to malloc | call to malloc | test.cpp:53:20:53:23 | size | size |
|
||||
| test.cpp:96:9:96:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:96:9:96:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
|
||||
| test.cpp:110:9:110:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:110:9:110:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
|
||||
| test.cpp:157:9:157:14 | Store: ... = ... | test.cpp:143:18:143:23 | call to malloc | test.cpp:157:9:157:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:143:18:143:23 | call to malloc | call to malloc | test.cpp:144:29:144:32 | size | size |
|
||||
| test.cpp:171:9:171:14 | Store: ... = ... | test.cpp:143:18:143:23 | call to malloc | test.cpp:171:9:171:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:143:18:143:23 | call to malloc | call to malloc | test.cpp:144:29:144:32 | size | size |
|
|
@ -0,0 +1 @@
|
|||
experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
|
|
@ -0,0 +1,191 @@
|
|||
char *malloc(int size);
|
||||
|
||||
void test1(int size) {
|
||||
char* p = malloc(size);
|
||||
char* q = p + size;
|
||||
char a = *q; // BAD
|
||||
char b = *(q - 1); // GOOD
|
||||
char c = *(q + 1); // BAD
|
||||
char d = *(q + size); // BAD [NOT DETECTED]
|
||||
char e = *(q - size); // GOOD
|
||||
char f = *(q + size + 1); // BAD [NOT DETECTED]
|
||||
char g = *(q - size - 1); // GOOD
|
||||
}
|
||||
|
||||
void test2(int size) {
|
||||
char* p = malloc(size);
|
||||
char* q = p + size - 1;
|
||||
char a = *q; // GOOD
|
||||
char b = *(q - 1); // GOOD
|
||||
char c = *(q + 1); // BAD
|
||||
char d = *(q + size); // BAD [NOT DETECTED]
|
||||
char e = *(q - size); // GOOD
|
||||
char f = *(q + size + 1); // BAD [NOT DETECTED]
|
||||
char g = *(q - size - 1); // GOOD
|
||||
}
|
||||
|
||||
void test3(int size) {
|
||||
char* p = malloc(size + 1);
|
||||
char* q = p + (size + 1);
|
||||
char a = *q; // BAD
|
||||
char b = *(q - 1); // GOOD
|
||||
char c = *(q + 1); // BAD
|
||||
char d = *(q + size); // BAD [NOT DETECTED]
|
||||
char e = *(q - size); // GOOD
|
||||
char f = *(q + size + 1); // BAD [NOT DETECTED]
|
||||
char g = *(q - size - 1); // GOOD
|
||||
}
|
||||
|
||||
void test4(int size) {
|
||||
char* p = malloc(size - 1);
|
||||
char* q = p + (size - 1);
|
||||
char a = *q; // BAD
|
||||
char b = *(q - 1); // GOOD
|
||||
char c = *(q + 1); // BAD
|
||||
char d = *(q + size); // BAD [NOT DETECTED]
|
||||
char e = *(q - size); // GOOD
|
||||
char f = *(q + size + 1); // BAD [NOT DETECTED]
|
||||
char g = *(q - size - 1); // GOOD
|
||||
}
|
||||
|
||||
char* mk_array(int size, char** end) {
|
||||
char* begin = malloc(size);
|
||||
*end = begin + size;
|
||||
|
||||
return begin;
|
||||
}
|
||||
|
||||
void test5(int size) {
|
||||
char* end;
|
||||
char* begin = mk_array(size, &end);
|
||||
|
||||
for (char* p = begin; p != end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
|
||||
for (char* p = begin; p <= end; ++p) {
|
||||
*p = 0; // BAD
|
||||
}
|
||||
|
||||
for (char* p = begin; p < end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
struct array_t {
|
||||
char* begin;
|
||||
char* end;
|
||||
};
|
||||
|
||||
array_t mk_array(int size) {
|
||||
array_t arr;
|
||||
arr.begin = malloc(size);
|
||||
arr.end = arr.begin + size;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
void test6(int size) {
|
||||
array_t arr = mk_array(size);
|
||||
|
||||
for (char* p = arr.begin; p != arr.end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
|
||||
for (char* p = arr.begin; p <= arr.end; ++p) {
|
||||
*p = 0; // BAD
|
||||
}
|
||||
|
||||
for (char* p = arr.begin; p < arr.end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test7_callee(array_t arr) {
|
||||
for (char* p = arr.begin; p != arr.end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
|
||||
for (char* p = arr.begin; p <= arr.end; ++p) {
|
||||
*p = 0; // BAD
|
||||
}
|
||||
|
||||
for (char* p = arr.begin; p < arr.end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test7(int size) {
|
||||
test7_callee(mk_array(size));
|
||||
}
|
||||
|
||||
void test8(int size) {
|
||||
array_t arr;
|
||||
char* p = malloc(size);
|
||||
arr.begin = p;
|
||||
arr.end = p + size;
|
||||
|
||||
for (int i = 0; i < arr.end - arr.begin; i++) {
|
||||
*(arr.begin + i) = 0; // GOOD
|
||||
}
|
||||
|
||||
for (int i = 0; i != arr.end - arr.begin; i++) {
|
||||
*(arr.begin + i) = 0; // GOOD
|
||||
}
|
||||
|
||||
for (int i = 0; i <= arr.end - arr.begin; i++) {
|
||||
*(arr.begin + i) = 0; // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
array_t *mk_array_p(int size) {
|
||||
array_t *arr = (array_t*) malloc(sizeof(array_t));
|
||||
arr->begin = malloc(size);
|
||||
arr->end = arr->begin + size;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
void test9(int size) {
|
||||
array_t *arr = mk_array_p(size);
|
||||
|
||||
for (char* p = arr->begin; p != arr->end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
|
||||
for (char* p = arr->begin; p <= arr->end; ++p) {
|
||||
*p = 0; // BAD
|
||||
}
|
||||
|
||||
for (char* p = arr->begin; p < arr->end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test10_callee(array_t *arr) {
|
||||
for (char* p = arr->begin; p != arr->end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
|
||||
for (char* p = arr->begin; p <= arr->end; ++p) {
|
||||
*p = 0; // BAD
|
||||
}
|
||||
|
||||
for (char* p = arr->begin; p < arr->end; ++p) {
|
||||
*p = 0; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test10(int size) {
|
||||
test10_callee(mk_array_p(size));
|
||||
}
|
||||
|
||||
void deref_plus_one(char* q) {
|
||||
char a = *(q + 1); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test11(unsigned size) {
|
||||
char *p = malloc(size);
|
||||
char *q = p + size - 1;
|
||||
deref_plus_one(q);
|
||||
}
|
|
@ -179,7 +179,15 @@ The select clause of each alert query defines the alert message that is displaye
|
|||
* The message should factually describe the problem that is being highlighted–it should not contain recommendations about how to fix the problem or value judgements.
|
||||
* Program element references should be in 'single quotes' to distinguish them from ordinary words. Quotes are not needed around substitutions (`$@`).
|
||||
* Avoid constant alert message strings and include some context, if possible. For example, `The class 'Foo' is duplicated as 'Bar'.` is preferable to `This class is duplicated here.`
|
||||
* If a reference to the current location can't be avoided use "this location" instead of "here". For example, `Bad thing at this location.` is preferable to `Bad thing here.`. This avoids the "click here" anti-pattern.
|
||||
* For path queries, if possible, try to follow the template: `This path depends on a [user-provided value].`, or alternatively (if the first option doesn't work) `[User-provided value] flows to this location and is used in a path.`.
|
||||
* Taint tracking queries generally have a sink that "depends on" the source, and dataflow queries generally have a source that "flows to" the sink.
|
||||
|
||||
### Links in alert messages
|
||||
|
||||
* Where you reference another program element, link to it if possible using a substitution (`$@`). Links should be used inline in the sentence, rather than as parenthesised lists or appositions.
|
||||
* Avoid using link texts that don't describe what they link to. For example, rewrite `This sensitive data is written to a logfile unescaped [here]` to `This sensitive data is [written to a logfile unescaped]`.
|
||||
* Make link text as concise and precise as possible. For example, avoid starting a link text with an indefinite article (a, an). `Path construction depends on a [user-provided value]` is preferable to `Path construction depends on [a user-provided value]`. (Where the square brackets indicate a link.) See [the W3C guide on link texts](https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html) for further information.
|
||||
* When a message contains multiple links, construct a sentence that has the most variable link (that is, the link with most targets) last. For further information, see [Defining the results of a query](https://codeql.github.com/docs/writing-codeql-queries/defining-the-results-of-a-query/).
|
||||
|
||||
For examples of select clauses and alert messages, see the query source files at the following pages:
|
||||
|
|
|
@ -29,5 +29,7 @@ module InsecureRandomness {
|
|||
|
||||
/** Holds if `sink` is a sink for this configuration with kind `kind`. */
|
||||
predicate isSink(Sink sink, string kind) { kind = sink.getKind() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,11 @@ import go
|
|||
|
||||
/** Provides classes and predicates shared between the XSS queries. */
|
||||
module SharedXss {
|
||||
/** A data flow source for XSS vulnerabilities. */
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
/**
|
||||
* DEPRECATED: This class is not used.
|
||||
* A data flow source for XSS vulnerabilities.
|
||||
*/
|
||||
abstract deprecated class Source extends DataFlow::Node { }
|
||||
|
||||
/** A data flow sink for XSS vulnerabilities. */
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The predicate `Annotation.getAValue()` has been deprecated because it might lead to obtaining the value of the wrong annotation element by accident. `getValue(string)` (or one of the value type specific predicates) should be used to explicitly specify the name of the annotation element.
|
||||
* The predicate `Annotation.getAValue(string)` has been renamed to `getAnArrayValue(string)`.
|
||||
* The predicate `SuppressWarningsAnnotation.getASuppressedWarningLiteral()` has been deprecated because it unnecessarily restricts the result type; `getASuppressedWarning()` should be used instead.
|
||||
* The predicates `TargetAnnotation.getATargetExpression()` and `RetentionAnnotation.getRetentionPolicyExpression()` have been deprecated because getting the enum constant read expression is rarely useful, instead the corresponding predicates for getting the name of the referenced enum constants should be used.
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
category: feature
|
||||
---
|
||||
* The predicates of the CodeQL class `Annotation` have been improved:
|
||||
* Convenience value type specific predicates have been added, such as `getEnumConstantValue(string)` or `getStringValue(string)`.
|
||||
* Convenience predicates for elements with array values have been added, such as `getAnEnumConstantArrayValue(string)`. While the behavior of the existing predicates has not changed, usage of them should be reviewed (or replaced with the newly added predicate) to make sure they work correctly for elements with array values.
|
||||
* Some internal CodeQL usage of the `Annotation` predicates has been adjusted and corrected; this might affect the results of some queries.
|
||||
* New predicates have been added to the CodeQL class `Annotatable` to support getting declared and associated annotations. As part of that, `hasAnnotation()` has been changed to also consider inherited annotations, to be consistent with `hasAnnotation(string, string)` and `getAnAnnotation()`. The newly added predicate `hasDeclaredAnnotation()` can be used as replacement for the old functionality.
|
||||
* New predicates have been added to the CodeQL class `AnnotationType` to simplify getting information about usage of JDK meta-annotations, such as `@Retention`.
|
|
@ -216,7 +216,7 @@ private predicate fixedHasLocation(Top l, Location loc, File f) {
|
|||
min(Location candidateLoc |
|
||||
hasLocation(l, candidateLoc)
|
||||
|
|
||||
candidateLoc order by candidateLoc.getFile().toString()
|
||||
candidateLoc order by candidateLoc.getFile().getAbsolutePath()
|
||||
) and
|
||||
not hasSourceLocation(l, _, _) and
|
||||
locations_default(loc, f, _, _, _, _)
|
||||
|
|
|
@ -44,12 +44,100 @@ class Annotation extends @annotation, Expr {
|
|||
result = this.getType().getAnnotationElement(name)
|
||||
}
|
||||
|
||||
/** Gets a value of an annotation element. */
|
||||
Expr getAValue() { filteredAnnotValue(this, _, result) }
|
||||
/**
|
||||
* DEPRECATED: Getting the value of _any_ annotation element is error-prone because
|
||||
* it could lead to selecting the value of the wrong element by accident (for example
|
||||
* when an annotation type is extended in the future). Prefer the predicate `getValue(string)`
|
||||
* and explicitly specify the element name. Use `getValue(_)` if it is really desired to
|
||||
* get the value of any element.
|
||||
*
|
||||
* Gets a value of an annotation element. This includes default values in case
|
||||
* no explicit value is specified. For elements with an array value type this
|
||||
* might have an `ArrayInit` as result. To properly handle array values, prefer
|
||||
* the predicate `getAnArrayValue`.
|
||||
*/
|
||||
deprecated Expr getAValue() { filteredAnnotValue(this, _, result) }
|
||||
|
||||
/** Gets the value of the annotation element with the specified `name`. */
|
||||
/**
|
||||
* Gets the value of the annotation element with the specified `name`.
|
||||
* This includes default values in case no explicit value is specified.
|
||||
* For elements with an array value type this might get an `ArrayInit` instance.
|
||||
* To properly handle array values, prefer the predicate `getAnArrayValue`.
|
||||
*/
|
||||
Expr getValue(string name) { filteredAnnotValue(this, this.getAnnotationElement(name), result) }
|
||||
|
||||
/**
|
||||
* Gets the value of the annotation element, if its type is not an array.
|
||||
* This guarantees that for consistency even elements of type array with a
|
||||
* single value have no result, to prevent accidental error-prone usage.
|
||||
*/
|
||||
private Expr getNonArrayValue(string name) {
|
||||
result = this.getValue(name) and
|
||||
not this.getAnnotationElement(name).getType() instanceof Array
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value type of the annotation element with the specified `name` is an enum type,
|
||||
* gets the enum constant used as value for that element. This includes default values in
|
||||
* case no explicit value is specified.
|
||||
*
|
||||
* If the element value type is an enum type array, use `getAnEnumConstantArrayValue`.
|
||||
*/
|
||||
EnumConstant getEnumConstantValue(string name) {
|
||||
result = this.getNonArrayValue(name).(FieldRead).getField()
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value type of the annotation element with the specified `name` is `String`,
|
||||
* gets the string value used for that element. This includes default values in case no
|
||||
* explicit value is specified.
|
||||
*
|
||||
* If the element value type is a string array, use `getAStringArrayValue`.
|
||||
*/
|
||||
string getStringValue(string name) {
|
||||
// Uses CompileTimeConstantExpr instead of StringLiteral because this can for example
|
||||
// be a read from a final variable as well.
|
||||
result = this.getNonArrayValue(name).(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value type of the annotation element with the specified `name` is `int` or
|
||||
* a smaller integral type or `char`, gets the int value used for that element.
|
||||
* This includes default values in case no explicit value is specified.
|
||||
*
|
||||
* If the element value type is an `int` array or an array of a smaller integral
|
||||
* type or `char`, use `getAnIntArrayValue`.
|
||||
*/
|
||||
int getIntValue(string name) {
|
||||
// Uses CompileTimeConstantExpr instead of IntegerLiteral because this can for example
|
||||
// be a read from a final variable as well.
|
||||
result = this.getNonArrayValue(name).(CompileTimeConstantExpr).getIntValue() and
|
||||
// Verify that type is integral; ignore floating point elements with IntegerLiteral as value
|
||||
this.getAnnotationElement(name).getType().hasName(["byte", "short", "int", "char"])
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value type of the annotation element with the specified `name` is `boolean`,
|
||||
* gets the boolean value used for that element. This includes default values in case
|
||||
* no explicit value is specified.
|
||||
*/
|
||||
boolean getBooleanValue(string name) {
|
||||
// Uses CompileTimeConstantExpr instead of BooleanLiteral because this can for example
|
||||
// be a read from a final variable as well.
|
||||
result = this.getNonArrayValue(name).(CompileTimeConstantExpr).getBooleanValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* If the value type of the annotation element with the specified `name` is `java.lang.Class`,
|
||||
* gets the type referred to by that `Class`. This includes default values in case no explicit
|
||||
* value is specified.
|
||||
*
|
||||
* If the element value type is a `Class` array, use `getATypeArrayValue`.
|
||||
*/
|
||||
Type getTypeValue(string name) {
|
||||
result = this.getNonArrayValue(name).(TypeLiteral).getReferencedType()
|
||||
}
|
||||
|
||||
/** Gets the element being annotated. */
|
||||
Element getTarget() { result = this.getAnnotatedElement() }
|
||||
|
||||
|
@ -60,16 +148,83 @@ class Annotation extends @annotation, Expr {
|
|||
|
||||
/**
|
||||
* Gets a value of the annotation element with the specified `name`, which must be declared as an array
|
||||
* type.
|
||||
* type. This includes default values in case no explicit value is specified.
|
||||
*
|
||||
* If the annotation element is defined with an array initializer, then the returned value will
|
||||
* be one of the elements of that array. Otherwise, the returned value will be the single
|
||||
* expression defined for the value.
|
||||
* If the annotation element is defined with an array initializer, then the result will be one of the
|
||||
* elements of that array. Otherwise, the result will be the single expression used as value.
|
||||
*/
|
||||
Expr getAValue(string name) {
|
||||
Expr getAnArrayValue(string name) { result = this.getArrayValue(name, _) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Predicate has been renamed to `getAnArrayValue`
|
||||
*/
|
||||
deprecated Expr getAValue(string name) { result = this.getAnArrayValue(name) }
|
||||
|
||||
/**
|
||||
* Gets a value of the annotation element with the specified `name`, which must be declared as an enum
|
||||
* type array. This includes default values in case no explicit value is specified.
|
||||
*
|
||||
* If the annotation element is defined with an array initializer, then the result will be one of the
|
||||
* elements of that array. Otherwise, the result will be the single expression used as value.
|
||||
*/
|
||||
EnumConstant getAnEnumConstantArrayValue(string name) {
|
||||
result = this.getAnArrayValue(name).(FieldRead).getField()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value of the annotation element with the specified `name`, which must be declared as a string
|
||||
* array. This includes default values in case no explicit value is specified.
|
||||
*
|
||||
* If the annotation element is defined with an array initializer, then the result will be one of the
|
||||
* elements of that array. Otherwise, the result will be the single expression used as value.
|
||||
*/
|
||||
string getAStringArrayValue(string name) {
|
||||
result = this.getAnArrayValue(name).(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value of the annotation element with the specified `name`, which must be declared as an `int`
|
||||
* array or an array of a smaller integral type or `char`. This includes default values in case no
|
||||
* explicit value is specified.
|
||||
*
|
||||
* If the annotation element is defined with an array initializer, then the result will be one of the
|
||||
* elements of that array. Otherwise, the result will be the single expression used as value.
|
||||
*/
|
||||
int getAnIntArrayValue(string name) {
|
||||
result = this.getAnArrayValue(name).(CompileTimeConstantExpr).getIntValue() and
|
||||
// Verify that type is integral; ignore floating point elements with IntegerLiteral as value
|
||||
this.getAnnotationElement(name).getType().hasName(["byte[]", "short[]", "int[]", "char[]"])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value of the annotation element with the specified `name`, which must be declared as a `Class`
|
||||
* array. This includes default values in case no explicit value is specified.
|
||||
*
|
||||
* If the annotation element is defined with an array initializer, then the result will be one of the
|
||||
* elements of that array. Otherwise, the result will be the single expression used as value.
|
||||
*/
|
||||
Type getATypeArrayValue(string name) {
|
||||
result = this.getAnArrayValue(name).(TypeLiteral).getReferencedType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value at a given index of the annotation element with the specified `name`, which must be
|
||||
* declared as an array type. This includes default values in case no explicit value is specified.
|
||||
*
|
||||
* If the annotation element is defined with an array initializer, then the result will be the element
|
||||
* at the given index of that array, starting at 0. Otherwise, the result will be the single expression
|
||||
* defined for the value and the `index` will be 0.
|
||||
*/
|
||||
Expr getArrayValue(string name, int index) {
|
||||
this.getType().getAnnotationElement(name).getType() instanceof Array and
|
||||
exists(Expr value | value = this.getValue(name) |
|
||||
if value instanceof ArrayInit then result = value.(ArrayInit).getAnInit() else result = value
|
||||
if value instanceof ArrayInit
|
||||
then
|
||||
// TODO: Currently reports incorrect index values in some cases, see https://github.com/github/codeql/issues/8645
|
||||
result = value.(ArrayInit).getInit(index)
|
||||
else (
|
||||
index = 0 and result = value
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -99,19 +254,86 @@ private predicate sourceAnnotValue(Annotation a, Method m, Expr val) {
|
|||
|
||||
/** An abstract representation of language elements that can be annotated. */
|
||||
class Annotatable extends Element {
|
||||
/** Holds if this element has an annotation. */
|
||||
predicate hasAnnotation() { exists(Annotation a | a.getAnnotatedElement() = this) }
|
||||
/**
|
||||
* Holds if this element has an annotation, including inherited annotations.
|
||||
* The retention policy of the annotation type is not considered.
|
||||
*/
|
||||
predicate hasAnnotation() { exists(this.getAnAnnotation()) }
|
||||
|
||||
/** Holds if this element has the specified annotation. */
|
||||
/**
|
||||
* Holds if this element has a declared annotation, excluding inherited annotations.
|
||||
* The retention policy of the annotation type is not considered.
|
||||
*/
|
||||
predicate hasDeclaredAnnotation() { exists(this.getADeclaredAnnotation()) }
|
||||
|
||||
/**
|
||||
* Holds if this element has the specified annotation, including inherited
|
||||
* annotations. The retention policy of the annotation type is not considered.
|
||||
*/
|
||||
predicate hasAnnotation(string package, string name) {
|
||||
exists(AnnotationType at | at = this.getAnAnnotation().getType() |
|
||||
at.nestedName() = name and at.getPackage().getName() = package
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an annotation that applies to this element. */
|
||||
/**
|
||||
* Gets an annotation that applies to this element, including inherited annotations.
|
||||
* The results only include _direct_ annotations; _indirect_ annotations, that is
|
||||
* repeated annotations in an (implicit) container annotation, are not included.
|
||||
* The retention policy of the annotation type is not considered.
|
||||
*/
|
||||
cached
|
||||
Annotation getAnAnnotation() { result.getAnnotatedElement() = this }
|
||||
Annotation getAnAnnotation() {
|
||||
// This predicate is overridden by Class to consider inherited annotations
|
||||
result = this.getADeclaredAnnotation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an annotation that is declared on this element, excluding inherited annotations.
|
||||
* The retention policy of the annotation type is not considered.
|
||||
*/
|
||||
Annotation getADeclaredAnnotation() { result.getAnnotatedElement() = this }
|
||||
|
||||
/** Gets an _indirect_ (= repeated) annotation. */
|
||||
private Annotation getAnIndirectAnnotation() {
|
||||
// 'indirect' as defined by https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/AnnotatedElement.html
|
||||
exists(AnnotationType t, Annotation containerAnn |
|
||||
t = result.getType() and
|
||||
containerAnn = this.getADeclaredAnnotation() and
|
||||
containerAnn.getType() = t.getContainingAnnotationType()
|
||||
|
|
||||
result = containerAnn.getAnArrayValue("value")
|
||||
)
|
||||
}
|
||||
|
||||
private Annotation getADeclaredAssociatedAnnotation(AnnotationType t) {
|
||||
// Direct or indirect annotation
|
||||
result.getType() = t and
|
||||
result = [this.getADeclaredAnnotation(), this.getAnIndirectAnnotation()]
|
||||
}
|
||||
|
||||
private Annotation getAnAssociatedAnnotation(AnnotationType t) {
|
||||
// 'associated' as defined by https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/AnnotatedElement.html
|
||||
if exists(this.getADeclaredAssociatedAnnotation(t))
|
||||
then result = this.getADeclaredAssociatedAnnotation(t)
|
||||
else (
|
||||
// Only if neither a direct nor an indirect annotation is present look for an inherited one
|
||||
t.isInherited() and
|
||||
// @Inherited only works for classes; cast to Annotatable is necessary because predicate is private
|
||||
result = this.(Class).getASupertype().(Class).(Annotatable).getAnAssociatedAnnotation(t)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an annotation _associated_ with this element, that is:
|
||||
* - An annotation directly present on this element, or
|
||||
* - An annotation indirectly present on this element (in the form of a repeated annotation), or
|
||||
* - If an annotation of a type is neither directly nor indirectly present
|
||||
* the result is an associated inherited annotation (recursively)
|
||||
*
|
||||
* The retention policy of the annotation type is not considered.
|
||||
*/
|
||||
Annotation getAnAssociatedAnnotation() { result = this.getAnAssociatedAnnotation(_) }
|
||||
|
||||
/**
|
||||
* Holds if this or any enclosing `Annotatable` has a `@SuppressWarnings("<category>")`
|
||||
|
@ -128,6 +350,11 @@ class Annotatable extends Element {
|
|||
or
|
||||
this.(NestedClass).getEnclosingType().suppressesWarningsAbout(category)
|
||||
or
|
||||
this.(LocalClassOrInterface)
|
||||
.getLocalTypeDeclStmt()
|
||||
.getEnclosingCallable()
|
||||
.suppressesWarningsAbout(category)
|
||||
or
|
||||
this.(LocalVariableDecl).getCallable().suppressesWarningsAbout(category)
|
||||
}
|
||||
}
|
||||
|
@ -146,10 +373,79 @@ class AnnotationType extends Interface {
|
|||
|
||||
/** Holds if this annotation type is annotated with the meta-annotation `@Inherited`. */
|
||||
predicate isInherited() {
|
||||
exists(Annotation ann |
|
||||
ann.getAnnotatedElement() = this and
|
||||
ann.getType().hasQualifiedName("java.lang.annotation", "Inherited")
|
||||
)
|
||||
this.getADeclaredAnnotation().getType().hasQualifiedName("java.lang.annotation", "Inherited")
|
||||
}
|
||||
|
||||
/** Holds if this annotation type is annotated with the meta-annotation `@Documented`. */
|
||||
predicate isDocumented() {
|
||||
this.getADeclaredAnnotation().getType().hasQualifiedName("java.lang.annotation", "Documented")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the retention policy of this annotation type, that is, the name of one of the
|
||||
* enum constants of `java.lang.annotation.RetentionPolicy`. If this annotation type
|
||||
* has no `@Retention` annotation, the result is `CLASS`.
|
||||
*/
|
||||
string getRetentionPolicy() {
|
||||
if this.getADeclaredAnnotation() instanceof RetentionAnnotation
|
||||
then result = this.getADeclaredAnnotation().(RetentionAnnotation).getRetentionPolicy()
|
||||
else
|
||||
// If not explicitly specified retention is CLASS
|
||||
result = "CLASS"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the element type is a possible target for this annotation type.
|
||||
* The `elementType` is the name of one of the `java.lang.annotation.ElementType`
|
||||
* enum constants.
|
||||
*
|
||||
* If this annotation type has no `@Target` annotation, it is considered to be applicable
|
||||
* in all declaration contexts. This matches the behavior of the latest Java versions
|
||||
* but differs from the behavior of older Java versions. This predicate must only be
|
||||
* called with names of `ElementType` enum constants; for other values it might hold
|
||||
* erroneously.
|
||||
*/
|
||||
bindingset[elementType]
|
||||
predicate isATargetType(string elementType) {
|
||||
/*
|
||||
* Note: Cannot use a predicate with string as result because annotation type without
|
||||
* explicit @Target can be applied in all declaration contexts, requiring to hardcode
|
||||
* element types here; then the results could become outdated if this predicate is not
|
||||
* updated for future JDK versions, or it could have irritating results, e.g. RECORD_COMPONENT
|
||||
* for a database created for Java 8.
|
||||
*
|
||||
* Could in theory read java.lang.annotation.ElementType constants from database, but might
|
||||
* be brittle in case ElementType is not present in the database for whatever reason.
|
||||
*/
|
||||
|
||||
if this.getADeclaredAnnotation() instanceof TargetAnnotation
|
||||
then elementType = this.getADeclaredAnnotation().(TargetAnnotation).getATargetElementType()
|
||||
else
|
||||
/*
|
||||
* Behavior for missing @Target annotation changed between Java versions. In older Java
|
||||
* versions it allowed usage in most (but not all) declaration contexts. Then for Java 14
|
||||
* JDK-8231435 changed it to allow usage in all declaration and type contexts. In Java 17
|
||||
* it was changed by JDK-8261610 to only allow usage in all declaration contexts, but not
|
||||
* in type contexts anymore. However, during these changes javac did not always comply with
|
||||
* the specification, see for example JDK-8254023.
|
||||
*
|
||||
* For simplicity pretend the latest behavior defined by the JLS applied in all versions;
|
||||
* that means any declaration context is allowed, but type contexts (represented by TYPE_USE,
|
||||
* see JLS 17 section 9.6.4.1) are not allowed.
|
||||
*/
|
||||
|
||||
elementType != "TYPE_USE"
|
||||
}
|
||||
|
||||
/** Holds if this annotation type is annotated with the meta-annotation `@Repeatable`. */
|
||||
predicate isRepeatable() { this.getADeclaredAnnotation() instanceof RepeatableAnnotation }
|
||||
|
||||
/**
|
||||
* If this annotation type is annotated with the meta-annotation `@Repeatable`,
|
||||
* gets the annotation type which acts as _containing annotation type_.
|
||||
*/
|
||||
AnnotationType getContainingAnnotationType() {
|
||||
result = this.getADeclaredAnnotation().(RepeatableAnnotation).getContainingType()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,8 @@ predicate depends(RefType t, RefType dep) {
|
|||
a.getAnnotatedElement().(Member).getDeclaringType() = t
|
||||
|
|
||||
usesType(a.getType(), dep) or
|
||||
usesType(a.getAValue().getType(), dep)
|
||||
usesType(a.getValue(_).getType(), dep) or
|
||||
usesType(a.getAnArrayValue(_).getType(), dep)
|
||||
)
|
||||
or
|
||||
// the type accessed in an `instanceof` expression in `t`.
|
||||
|
|
|
@ -90,7 +90,7 @@ predicate numDepends(RefType t, RefType dep, int value) {
|
|||
|
|
||||
elem = a and usesType(a.getType(), dep)
|
||||
or
|
||||
elem = a.getAValue() and
|
||||
elem = [a.getValue(_), a.getAnArrayValue(_)] and
|
||||
elem.getFile().getExtension() = "java" and
|
||||
usesType(elem.(Expr).getType(), dep)
|
||||
)
|
||||
|
|
|
@ -18,14 +18,16 @@ class OverrideAnnotation extends Annotation {
|
|||
class SuppressWarningsAnnotation extends Annotation {
|
||||
SuppressWarningsAnnotation() { this.getType().hasQualifiedName("java.lang", "SuppressWarnings") }
|
||||
|
||||
/** Gets the `StringLiteral` of a warning suppressed by this annotation. */
|
||||
StringLiteral getASuppressedWarningLiteral() {
|
||||
result = this.getAValue() or
|
||||
result = this.getAValue().(ArrayInit).getAnInit()
|
||||
}
|
||||
/**
|
||||
* DEPRECATED: This predicate restricts the results to `StringLiteral`; prefer `getASuppressedWarning()`
|
||||
* to get the name of a suppressed warning.
|
||||
*
|
||||
* Gets the `StringLiteral` of a warning suppressed by this annotation.
|
||||
*/
|
||||
deprecated StringLiteral getASuppressedWarningLiteral() { result = this.getAnArrayValue("value") }
|
||||
|
||||
/** Gets the name of a warning suppressed by this annotation. */
|
||||
string getASuppressedWarning() { result = this.getASuppressedWarningLiteral().getValue() }
|
||||
string getASuppressedWarning() { result = this.getAStringArrayValue("value") }
|
||||
}
|
||||
|
||||
/** A `@Target` annotation. */
|
||||
|
@ -33,18 +35,15 @@ class TargetAnnotation extends Annotation {
|
|||
TargetAnnotation() { this.getType().hasQualifiedName("java.lang.annotation", "Target") }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Getting the field access expression is rarely useful. Use `getATargetElementType()`
|
||||
* to get the name of the target element.
|
||||
*
|
||||
* Gets a target expression within this annotation.
|
||||
*
|
||||
* For example, the field access `ElementType.FIELD` is a target expression in
|
||||
* `@Target({ElementType.FIELD, ElementType.METHOD})`.
|
||||
*/
|
||||
Expr getATargetExpression() {
|
||||
not result instanceof ArrayInit and
|
||||
(
|
||||
result = this.getAValue() or
|
||||
result = this.getAValue().(ArrayInit).getAnInit()
|
||||
)
|
||||
}
|
||||
deprecated Expr getATargetExpression() { result = this.getAnArrayValue("value") }
|
||||
|
||||
/**
|
||||
* Gets the name of a target element type.
|
||||
|
@ -52,14 +51,7 @@ class TargetAnnotation extends Annotation {
|
|||
* For example, `METHOD` is the name of a target element type in
|
||||
* `@Target({ElementType.FIELD, ElementType.METHOD})`.
|
||||
*/
|
||||
string getATargetElementType() {
|
||||
exists(EnumConstant ec |
|
||||
ec = this.getATargetExpression().(VarAccess).getVariable() and
|
||||
ec.getDeclaringType().hasQualifiedName("java.lang.annotation", "ElementType")
|
||||
|
|
||||
result = ec.getName()
|
||||
)
|
||||
}
|
||||
string getATargetElementType() { result = this.getAnEnumConstantArrayValue("value").getName() }
|
||||
}
|
||||
|
||||
/** A `@Retention` annotation. */
|
||||
|
@ -67,12 +59,15 @@ class RetentionAnnotation extends Annotation {
|
|||
RetentionAnnotation() { this.getType().hasQualifiedName("java.lang.annotation", "Retention") }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Getting the field access expression is rarely useful. Use `getRetentionPolicy()`
|
||||
* to get the name of the retention policy.
|
||||
*
|
||||
* Gets the retention policy expression within this annotation.
|
||||
*
|
||||
* For example, the field access `RetentionPolicy.RUNTIME` is the
|
||||
* retention policy expression in `@Retention(RetentionPolicy.RUNTIME)`.
|
||||
*/
|
||||
Expr getRetentionPolicyExpression() { result = this.getValue("value") }
|
||||
deprecated Expr getRetentionPolicyExpression() { result = this.getValue("value") }
|
||||
|
||||
/**
|
||||
* Gets the name of the retention policy of this annotation.
|
||||
|
@ -80,14 +75,18 @@ class RetentionAnnotation extends Annotation {
|
|||
* For example, `RUNTIME` is the name of the retention policy
|
||||
* in `@Retention(RetentionPolicy.RUNTIME)`.
|
||||
*/
|
||||
string getRetentionPolicy() {
|
||||
exists(EnumConstant ec |
|
||||
ec = this.getRetentionPolicyExpression().(VarAccess).getVariable() and
|
||||
ec.getDeclaringType().hasQualifiedName("java.lang.annotation", "RetentionPolicy")
|
||||
|
|
||||
result = ec.getName()
|
||||
)
|
||||
}
|
||||
string getRetentionPolicy() { result = this.getEnumConstantValue("value").getName() }
|
||||
}
|
||||
|
||||
/** A `@Repeatable` annotation. */
|
||||
class RepeatableAnnotation extends Annotation {
|
||||
RepeatableAnnotation() { this.getType().hasQualifiedName("java.lang.annotation", "Repeatable") }
|
||||
|
||||
/**
|
||||
* Gets the annotation type which acts as _containing type_, grouping multiple
|
||||
* repeatable annotations together.
|
||||
*/
|
||||
AnnotationType getContainingType() { result = this.getTypeValue("value") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,11 +118,7 @@ abstract class NonReflectiveAnnotation extends Annotation { }
|
|||
|
||||
library class StandardNonReflectiveAnnotation extends NonReflectiveAnnotation {
|
||||
StandardNonReflectiveAnnotation() {
|
||||
exists(AnnotationType anntp | anntp = this.getType() |
|
||||
anntp.hasQualifiedName("java.lang", "Override") or
|
||||
anntp.hasQualifiedName("java.lang", "Deprecated") or
|
||||
anntp.hasQualifiedName("java.lang", "SuppressWarnings") or
|
||||
anntp.hasQualifiedName("java.lang", "SafeVarargs")
|
||||
)
|
||||
this.getType()
|
||||
.hasQualifiedName("java.lang", ["Override", "Deprecated", "SuppressWarnings", "SafeVarargs"])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ private newtype TPrintAstNode =
|
|||
shouldPrint(lvde, _) and lvde.getParent() instanceof SingleLocalVarDeclParent
|
||||
} or
|
||||
TAnnotationsNode(Annotatable ann) {
|
||||
shouldPrint(ann, _) and ann.hasAnnotation() and not partOfAnnotation(ann)
|
||||
shouldPrint(ann, _) and ann.hasDeclaredAnnotation() and not partOfAnnotation(ann)
|
||||
} or
|
||||
TParametersNode(Callable c) { shouldPrint(c, _) and not c.hasNoParameters() } or
|
||||
TBaseTypesNode(ClassOrInterface ty) { shouldPrint(ty, _) } or
|
||||
|
|
|
@ -161,15 +161,13 @@ class TestNGTestMethod extends Method {
|
|||
exists(TestNGTestAnnotation testAnnotation |
|
||||
testAnnotation = this.getAnAnnotation() and
|
||||
// The data provider must have the same name as the referenced data provider
|
||||
result.getDataProviderName() =
|
||||
testAnnotation.getValue("dataProvider").(StringLiteral).getValue()
|
||||
result.getDataProviderName() = testAnnotation.getStringValue("dataProvider")
|
||||
|
|
||||
// Either the data provider should be on the current class, or a supertype
|
||||
this.getDeclaringType().getAnAncestor() = result.getDeclaringType()
|
||||
or
|
||||
// Or the data provider class should be declared
|
||||
result.getDeclaringType() =
|
||||
testAnnotation.getValue("dataProviderClass").(TypeLiteral).getReferencedType()
|
||||
result.getDeclaringType() = testAnnotation.getTypeValue("dataProviderClass")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -227,9 +225,7 @@ class TestNGListenersAnnotation extends TestNGAnnotation {
|
|||
/**
|
||||
* Gets a listener defined in this annotation.
|
||||
*/
|
||||
TestNGListenerImpl getAListener() {
|
||||
result = this.getAValue("value").(TypeLiteral).getReferencedType()
|
||||
}
|
||||
TestNGListenerImpl getAListener() { result = this.getATypeArrayValue("value") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -60,7 +60,7 @@ class JaxbType extends Class {
|
|||
this.getAnAnnotation() = a and
|
||||
a.getType().(JaxbAnnotationType).hasName("XmlAccessorType")
|
||||
|
|
||||
result.getAnAccess() = a.getValue("value")
|
||||
result = a.getEnumConstantValue("value")
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -64,5 +64,5 @@ class RunWithAnnotation extends Annotation {
|
|||
/**
|
||||
* Gets the runner that will be used.
|
||||
*/
|
||||
Type getRunner() { result = this.getValue("value").(TypeLiteral).getReferencedType() }
|
||||
Type getRunner() { result = this.getTypeValue("value") }
|
||||
}
|
||||
|
|
|
@ -296,11 +296,7 @@ class JaxRSProducesAnnotation extends JaxRSAnnotation {
|
|||
/**
|
||||
* Gets a declared content type that can be produced by this resource.
|
||||
*/
|
||||
Expr getADeclaredContentTypeExpr() {
|
||||
result = this.getAValue() and not result instanceof ArrayInit
|
||||
or
|
||||
result = this.getAValue().(ArrayInit).getAnInit()
|
||||
}
|
||||
Expr getADeclaredContentTypeExpr() { result = this.getAnArrayValue("value") }
|
||||
}
|
||||
|
||||
/** An `@Consumes` annotation that describes content types can be consumed by this resource. */
|
||||
|
|
|
@ -85,9 +85,7 @@ class IbatisSqlOperationAnnotation extends Annotation {
|
|||
/**
|
||||
* Gets this annotation's SQL statement string.
|
||||
*/
|
||||
string getSqlValue() {
|
||||
result = this.getAValue("value").(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
string getSqlValue() { result = this.getAStringArrayValue("value") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,12 +33,12 @@ class PersistentEntity extends RefType {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the access type for this entity as defined by a `@javax.persistence.Access` annotation, if any.
|
||||
* Gets the access type for this entity as defined by a `@javax.persistence.Access` annotation,
|
||||
* if any, in lower case.
|
||||
*/
|
||||
string getAccessTypeFromAnnotation() {
|
||||
exists(AccessAnnotation accessType | accessType = this.getAnAnnotation() |
|
||||
result =
|
||||
accessType.getValue("value").(FieldRead).getField().(EnumConstant).getName().toLowerCase()
|
||||
result = accessType.getEnumConstantValue("value").getName().toLowerCase()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -311,9 +311,7 @@ class SpringQualifierDefinitionAnnotation extends Annotation {
|
|||
/**
|
||||
* Gets the value of the qualifier field for this qualifier.
|
||||
*/
|
||||
string getQualifierValue() {
|
||||
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
string getQualifierValue() { result = this.getStringValue("value") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -325,9 +323,7 @@ class SpringQualifierAnnotation extends Annotation {
|
|||
/**
|
||||
* Gets the value of the qualifier field for this qualifier.
|
||||
*/
|
||||
string getQualifierValue() {
|
||||
result = this.getValue("value").(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
string getQualifierValue() { result = this.getStringValue("value") }
|
||||
|
||||
/**
|
||||
* Gets the bean definition in an XML file that this qualifier resolves to, if any.
|
||||
|
@ -350,9 +346,7 @@ class SpringResourceAnnotation extends Annotation {
|
|||
/**
|
||||
* Gets the specified name value, if any.
|
||||
*/
|
||||
string getNameValue() {
|
||||
result = this.getValue("name").(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
string getNameValue() { result = this.getStringValue("name") }
|
||||
|
||||
/**
|
||||
* Gets the bean definition in an XML file that the resource resolves to, if any.
|
||||
|
|
|
@ -40,16 +40,10 @@ class SpringComponentScan extends Annotation {
|
|||
*/
|
||||
string getBasePackages() {
|
||||
// "value" and "basePackages" are synonymous, and are simple strings
|
||||
result = this.getAValue("basePackages").(StringLiteral).getValue()
|
||||
result = this.getAStringArrayValue(["basePackages", "value"])
|
||||
or
|
||||
result = this.getAValue("value").(StringLiteral).getValue()
|
||||
or
|
||||
exists(TypeLiteral typeLiteral |
|
||||
// Base package classes are type literals whose package should be considered a base package.
|
||||
typeLiteral = this.getAValue("basePackageClasses")
|
||||
|
|
||||
result = typeLiteral.getReferencedType().(RefType).getPackage().getName()
|
||||
)
|
||||
// Base package classes are type literals whose package should be considered a base package.
|
||||
result = this.getATypeArrayValue("basePackageClasses").(RefType).getPackage().getName()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,8 +138,7 @@ class SpringComponent extends RefType {
|
|||
if exists(this.getComponentAnnotation().getValue("value"))
|
||||
then
|
||||
// If the name has been specified in the component annotation, use that.
|
||||
result =
|
||||
this.getComponentAnnotation().getValue("value").(CompileTimeConstantExpr).getStringValue()
|
||||
result = this.getComponentAnnotation().getStringValue("value")
|
||||
else
|
||||
// Otherwise use the name of the class, with the initial letter lower cased.
|
||||
exists(string name | name = this.getName() |
|
||||
|
@ -204,7 +197,7 @@ class SpringComponent extends RefType {
|
|||
.getType()
|
||||
.hasQualifiedName("org.springframework.context.annotation", "Profile")
|
||||
|
|
||||
result = profileAnnotation.getAValue("value").(StringLiteral).getValue()
|
||||
result = profileAnnotation.getAStringArrayValue("value")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,9 +154,7 @@ class SpringRequestMappingMethod extends SpringControllerMethod {
|
|||
}
|
||||
|
||||
/** Gets the "value" @RequestMapping annotation value, if present. */
|
||||
string getValue() {
|
||||
result = requestMappingAnnotation.getValue("value").(CompileTimeConstantExpr).getStringValue()
|
||||
}
|
||||
string getValue() { result = requestMappingAnnotation.getStringValue("value") }
|
||||
|
||||
/** Holds if this is considered an `@ResponseBody` method. */
|
||||
predicate isResponseBody() {
|
||||
|
|
|
@ -34,5 +34,5 @@ class StrutsActionsAnnotation extends StrutsAnnotation {
|
|||
/**
|
||||
* Gets an Action annotation contained in this Actions annotation.
|
||||
*/
|
||||
StrutsActionAnnotation getAnAction() { result = this.getAValue("value") }
|
||||
StrutsActionAnnotation getAnAction() { result = this.getAnArrayValue("value") }
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class SuppressionAnnotation extends SuppressWarningsAnnotation {
|
|||
string text;
|
||||
|
||||
SuppressionAnnotation() {
|
||||
text = this.getASuppressedWarningLiteral().getValue() and
|
||||
text = this.getASuppressedWarning() and
|
||||
exists(getAnnotationText(text))
|
||||
}
|
||||
|
||||
|
|
|
@ -19,8 +19,5 @@ where
|
|||
m.getNumberOfParameters() = 1 and
|
||||
c.getArgument(0).getType() = p and
|
||||
p.getATypeArgument() = t and
|
||||
not exists(RetentionAnnotation a |
|
||||
t.getAnAnnotation() = a and
|
||||
a.getAValue().(VarAccess).getVariable().hasName("RUNTIME")
|
||||
)
|
||||
t.getRetentionPolicy() != "RUNTIME"
|
||||
select c, "Call to isAnnotationPresent where no annotation has the RUNTIME retention policy."
|
||||
|
|
|
@ -47,8 +47,8 @@ class SpringControllerRequestMappingGetMethod extends SpringControllerGetMethod
|
|||
.getType()
|
||||
.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
|
||||
(
|
||||
this.getAnAnnotation().getValue("method").(VarAccess).getVariable().getName() = "GET" or
|
||||
this.getAnAnnotation().getValue("method").(ArrayInit).getSize() = 0 //Java code example: @RequestMapping(value = "test")
|
||||
this.getAnAnnotation().getAnEnumConstantArrayValue("method").getName() = "GET" or
|
||||
not exists(this.getAnAnnotation().getAnArrayValue("method")) //Java code example: @RequestMapping(value = "test")
|
||||
) and
|
||||
not this.getAParamType().getName() = "MultipartFile"
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ from Field f, Annotation ann, Expr value, Expr valueChild
|
|||
where
|
||||
f.getDeclaringType().fromSource() and
|
||||
ann = f.getAnAnnotation() and
|
||||
value = ann.getAValue() and
|
||||
value = ann.getValue(_) and
|
||||
valueChild.getParent() = value
|
||||
select f, ann, value, valueChild
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
declaredAnnotation
|
||||
| Annotatable.java:8:16:8:40 | CustomInheritedAnnotation | Annotatable.java:7:5:7:14 | Inherited |
|
||||
| Annotatable.java:14:11:14:22 | WithDeclared | Annotatable.java:12:5:12:21 | CustomAnnotation |
|
||||
| Annotatable.java:14:11:14:22 | WithDeclared | Annotatable.java:13:5:13:38 | CustomInheritedAnnotation |
|
||||
| Annotatable.java:17:14:17:31 | methodWithDeclared | Annotatable.java:16:9:16:49 | CustomInheritedAnnotation |
|
||||
| Annotatable.java:22:14:22:31 | methodWithDeclared | Annotatable.java:21:9:21:17 | Override |
|
||||
| Annotatable.java:30:11:30:41 | SubclassDeclaringSameAnnotation | Annotatable.java:29:5:29:37 | CustomInheritedAnnotation |
|
||||
| Annotatable.java:35:15:35:35 | InterfaceWithDeclared | Annotatable.java:34:5:34:38 | CustomInheritedAnnotation |
|
||||
| Annotatable.java:47:16:47:35 | RepeatableAnnotation | Annotatable.java:46:5:46:42 | Repeatable |
|
||||
| Annotatable.java:52:16:52:43 | InheritedContainerAnnotation | Annotatable.java:51:5:51:14 | Inherited |
|
||||
| Annotatable.java:58:16:58:44 | InheritedRepeatableAnnotation | Annotatable.java:56:5:56:14 | Inherited |
|
||||
| Annotatable.java:58:16:58:44 | InheritedRepeatableAnnotation | Annotatable.java:57:5:57:51 | Repeatable |
|
||||
| Annotatable.java:63:16:63:44 | InheritedContainerAnnotation2 | Annotatable.java:62:5:62:14 | Inherited |
|
||||
| Annotatable.java:71:16:71:47 | NonInheritedRepeatableAnnotation | Annotatable.java:70:5:70:52 | Repeatable |
|
||||
| Annotatable.java:78:11:78:30 | WithAssociatedSingle | Annotatable.java:75:5:75:33 | RepeatableAnnotation |
|
||||
| Annotatable.java:78:11:78:30 | WithAssociatedSingle | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:78:11:78:30 | WithAssociatedSingle | Annotatable.java:77:5:77:45 | NonInheritedRepeatableAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | ContainerAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
|
||||
| Annotatable.java:92:11:92:44 | SubclassOfSingleWithEmptyContainer | Annotatable.java:90:5:90:37 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:92:11:92:44 | SubclassOfSingleWithEmptyContainer | Annotatable.java:91:5:91:38 | InheritedContainerAnnotation2 |
|
||||
| Annotatable.java:98:15:98:37 | InterfaceWithAssociated | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | ContainerAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
|
||||
| Annotatable.java:116:14:116:39 | methodWithAssociatedSingle | Annotatable.java:115:9:115:53 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:121:14:121:33 | methodWithAssociated | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:126:14:126:39 | methodWithAssociatedSingle | Annotatable.java:125:9:125:17 | Override |
|
||||
| Annotatable.java:129:14:129:33 | methodWithAssociated | Annotatable.java:128:9:128:17 | Override |
|
||||
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.java:135:5:135:34 | RepeatableAnnotation |
|
||||
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.java:136:5:136:43 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.java:137:5:137:46 | NonInheritedRepeatableAnnotation |
|
||||
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.java:142:5:142:37 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.java:143:5:143:38 | InheritedContainerAnnotation2 |
|
||||
| Annotatable.java:150:35:153:5 | {...} | Annotatable.java:151:9:151:53 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:150:35:153:5 | {...} | Annotatable.java:152:9:152:53 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:148:5:148:44 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:150:5:153:6 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:160:16:160:41 | NestedAnnotationContainer1 | Annotatable.java:159:5:159:14 | Inherited |
|
||||
| Annotatable.java:166:16:166:32 | NestedAnnotation1 | Annotatable.java:164:5:164:14 | Inherited |
|
||||
| Annotatable.java:166:16:166:32 | NestedAnnotation1 | Annotatable.java:165:5:165:49 | Repeatable |
|
||||
| Annotatable.java:172:16:172:32 | NestedAnnotation2 | Annotatable.java:170:5:170:14 | Inherited |
|
||||
| Annotatable.java:172:16:172:32 | NestedAnnotation2 | Annotatable.java:171:5:171:40 | Repeatable |
|
||||
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
|
||||
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.java:177:5:177:27 | NestedAnnotation2 |
|
||||
| Annotatable.java:189:11:189:48 | WithNestedAssociatedExplicitContainers | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
|
||||
annotationAdditional
|
||||
| Annotatable.java:20:11:20:18 | Subclass | Annotatable.java:13:5:13:38 | CustomInheritedAnnotation |
|
||||
| Annotatable.java:26:11:26:21 | SubSubclass | Annotatable.java:13:5:13:38 | CustomInheritedAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:92:11:92:44 | SubclassOfSingleWithEmptyContainer | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
|
||||
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
|
||||
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:138:11:138:28 | SubclassWithSingle | Annotatable.class:0:0:0:0 | InheritedContainerAnnotation2 |
|
||||
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:148:5:148:44 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:150:5:153:6 | InheritedContainerAnnotation |
|
||||
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
|
||||
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.java:177:5:177:27 | NestedAnnotation2 |
|
||||
| Annotatable.java:191:11:191:56 | WithNestedAssociatedExplicitContainersSubclass | Annotatable.class:0:0:0:0 | NestedAnnotationContainer1 |
|
||||
bugAnnotationAdditional
|
||||
associatedAnnotationAdditional
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
|
||||
| Annotatable.java:98:15:98:37 | InterfaceWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:98:15:98:37 | InterfaceWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | NonInheritedRepeatableAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
|
||||
| Annotatable.java:113:11:113:32 | WithAssociatedMultiple | Annotatable.class:0:0:0:0 | RepeatableAnnotation |
|
||||
| Annotatable.java:121:14:121:33 | methodWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:121:14:121:33 | methodWithAssociated | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:124:11:124:32 | WithAssociatedSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:133:11:133:35 | WithAssociatedSubSubclass | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:144:11:144:46 | SubclassOfMultipleWithEmptyContainer | Annotatable.class:0:0:0:0 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:151:9:151:53 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:154:11:154:45 | ExplicitContainerAndSingleContained | Annotatable.java:152:9:152:53 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:151:9:151:53 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:156:11:156:35 | ExplicitContainerSubclass | Annotatable.java:152:9:152:53 | InheritedRepeatableAnnotation |
|
||||
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
| Annotatable.java:182:11:182:30 | WithNestedAssociated | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
| Annotatable.java:184:11:184:38 | WithNestedAssociatedSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
| Annotatable.java:189:11:189:48 | WithNestedAssociatedExplicitContainers | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
| Annotatable.java:189:11:189:48 | WithNestedAssociatedExplicitContainers | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
| Annotatable.java:191:11:191:56 | WithNestedAssociatedExplicitContainersSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
| Annotatable.java:191:11:191:56 | WithNestedAssociatedExplicitContainersSubclass | Annotatable.class:0:0:0:0 | NestedAnnotation1 |
|
||||
associatedAnnotationNotInherited
|
||||
| Annotatable.java:86:11:86:30 | SubclassWithMultiple | Annotatable.java:76:5:76:42 | InheritedRepeatableAnnotation |
|
|
@ -0,0 +1,192 @@
|
|||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
|
||||
class Annotatable {
|
||||
@interface CustomAnnotation {}
|
||||
|
||||
@Inherited
|
||||
@interface CustomInheritedAnnotation {
|
||||
String value();
|
||||
}
|
||||
|
||||
@CustomAnnotation
|
||||
@CustomInheritedAnnotation("base")
|
||||
class WithDeclared {
|
||||
// Annotations on methods are not inherited
|
||||
@CustomInheritedAnnotation("base-method")
|
||||
void methodWithDeclared() {}
|
||||
}
|
||||
|
||||
class Subclass extends WithDeclared {
|
||||
@Override
|
||||
void methodWithDeclared() {}
|
||||
}
|
||||
|
||||
// Inheritance from super-superclass
|
||||
class SubSubclass extends Subclass {}
|
||||
|
||||
// Prevents inheriting annotation of same type
|
||||
@CustomInheritedAnnotation("sub")
|
||||
class SubclassDeclaringSameAnnotation extends WithDeclared {}
|
||||
|
||||
|
||||
// Annotations on interfaces are not inherited
|
||||
@CustomInheritedAnnotation("base")
|
||||
interface InterfaceWithDeclared {}
|
||||
|
||||
interface ExtendingInterface extends InterfaceWithDeclared {}
|
||||
|
||||
class ImplementingInterface implements InterfaceWithDeclared {}
|
||||
|
||||
|
||||
@interface ContainerAnnotation {
|
||||
RepeatableAnnotation[] value();
|
||||
}
|
||||
|
||||
@Repeatable(ContainerAnnotation.class)
|
||||
@interface RepeatableAnnotation {
|
||||
String value();
|
||||
}
|
||||
|
||||
@Inherited
|
||||
@interface InheritedContainerAnnotation {
|
||||
InheritedRepeatableAnnotation[] value();
|
||||
}
|
||||
|
||||
@Inherited
|
||||
@Repeatable(InheritedContainerAnnotation.class)
|
||||
@interface InheritedRepeatableAnnotation {
|
||||
String value();
|
||||
}
|
||||
|
||||
@Inherited
|
||||
@interface InheritedContainerAnnotation2 {
|
||||
NonInheritedRepeatableAnnotation[] value();
|
||||
}
|
||||
|
||||
// Container is marked as @Inherited, but this annotation type is not
|
||||
// This is allowed, but means that associated annotations will not be inherited,
|
||||
// see java.lang.reflect.AnnotatedElement documentation
|
||||
@Repeatable(InheritedContainerAnnotation2.class)
|
||||
@interface NonInheritedRepeatableAnnotation {
|
||||
String value();
|
||||
}
|
||||
|
||||
@RepeatableAnnotation("base")
|
||||
@InheritedRepeatableAnnotation("base")
|
||||
@NonInheritedRepeatableAnnotation("base")
|
||||
class WithAssociatedSingle {}
|
||||
|
||||
@RepeatableAnnotation("sub-1")
|
||||
@RepeatableAnnotation("sub-2")
|
||||
@InheritedRepeatableAnnotation("sub-1")
|
||||
@InheritedRepeatableAnnotation("sub-2")
|
||||
@NonInheritedRepeatableAnnotation("sub-1")
|
||||
@NonInheritedRepeatableAnnotation("sub-2")
|
||||
class SubclassWithMultiple extends WithAssociatedSingle {}
|
||||
|
||||
|
||||
// Empty container annotations have no effect; annotations are inherited from superclass
|
||||
@InheritedContainerAnnotation({})
|
||||
@InheritedContainerAnnotation2({})
|
||||
class SubclassOfSingleWithEmptyContainer extends WithAssociatedSingle {}
|
||||
|
||||
|
||||
// Annotations on interfaces are not inherited
|
||||
@InheritedRepeatableAnnotation("base-1")
|
||||
@InheritedRepeatableAnnotation("base-2")
|
||||
interface InterfaceWithAssociated {}
|
||||
|
||||
interface ExtendingInterfaceWithAssociated extends InterfaceWithAssociated {}
|
||||
|
||||
class ImplementingInterfaceWithAssociated implements InterfaceWithAssociated {}
|
||||
|
||||
|
||||
@RepeatableAnnotation("base-1")
|
||||
@RepeatableAnnotation("base-2")
|
||||
@InheritedRepeatableAnnotation("base-1")
|
||||
@InheritedRepeatableAnnotation("base-2")
|
||||
// These annotations are not inherited, but their (implicit) container annotation
|
||||
// is inherited
|
||||
@NonInheritedRepeatableAnnotation("base-1")
|
||||
@NonInheritedRepeatableAnnotation("base-2")
|
||||
class WithAssociatedMultiple {
|
||||
// Annotations on methods are not inherited
|
||||
@InheritedRepeatableAnnotation("base-method")
|
||||
void methodWithAssociatedSingle() {}
|
||||
|
||||
// Annotations on methods are not inherited
|
||||
@InheritedRepeatableAnnotation("base-method-1")
|
||||
@InheritedRepeatableAnnotation("base-method-2")
|
||||
void methodWithAssociated() {}
|
||||
}
|
||||
|
||||
class WithAssociatedSubclass extends WithAssociatedMultiple {
|
||||
@Override
|
||||
void methodWithAssociatedSingle() {}
|
||||
|
||||
@Override
|
||||
void methodWithAssociated() {}
|
||||
}
|
||||
|
||||
// Inheritance from super-superclass
|
||||
class WithAssociatedSubSubclass extends WithAssociatedSubclass {}
|
||||
|
||||
@RepeatableAnnotation("sub-1")
|
||||
@InheritedRepeatableAnnotation("sub-1")
|
||||
@NonInheritedRepeatableAnnotation("sub-1")
|
||||
class SubclassWithSingle extends WithAssociatedMultiple {}
|
||||
|
||||
|
||||
// Empty container annotations have no effect; associated annotations are inherited from superclass
|
||||
@InheritedContainerAnnotation({})
|
||||
@InheritedContainerAnnotation2({})
|
||||
class SubclassOfMultipleWithEmptyContainer extends WithAssociatedMultiple {}
|
||||
|
||||
|
||||
// This annotation exists on its own without a container
|
||||
@InheritedRepeatableAnnotation("single")
|
||||
// TODO: Has currently spurious results for ArrayInit due to https://github.com/github/codeql/issues/8647
|
||||
@InheritedContainerAnnotation({
|
||||
@InheritedRepeatableAnnotation("container-1"),
|
||||
@InheritedRepeatableAnnotation("container-2")
|
||||
})
|
||||
class ExplicitContainerAndSingleContained {}
|
||||
|
||||
class ExplicitContainerSubclass extends ExplicitContainerAndSingleContained {}
|
||||
|
||||
|
||||
@Inherited
|
||||
@interface NestedAnnotationContainer1 {
|
||||
NestedAnnotation1[] value();
|
||||
}
|
||||
|
||||
@Inherited
|
||||
@Repeatable(NestedAnnotationContainer1.class)
|
||||
@interface NestedAnnotation1 {
|
||||
NestedAnnotation2[] value();
|
||||
}
|
||||
|
||||
@Inherited
|
||||
@Repeatable(NestedAnnotation1.class)
|
||||
@interface NestedAnnotation2 {
|
||||
String value();
|
||||
}
|
||||
|
||||
// This annotation exists on its own without a container
|
||||
@NestedAnnotation2("1")
|
||||
// But these are nested inside an implicit @NestedAnnotationContainer1
|
||||
// Nested repeated annotations (@NestedAnnotation2) are not considered associated
|
||||
@NestedAnnotation1({@NestedAnnotation2("1-1"), @NestedAnnotation2("1-2")})
|
||||
@NestedAnnotation1({@NestedAnnotation2("2-1"), @NestedAnnotation2("2-2")})
|
||||
class WithNestedAssociated {}
|
||||
|
||||
class WithNestedAssociatedSubclass extends WithNestedAssociated {}
|
||||
|
||||
// Nested repeated annotations (@NestedAnnotation2) are not considered associated
|
||||
@NestedAnnotation1({@NestedAnnotation2("1-1"), @NestedAnnotation2("1-2")})
|
||||
@NestedAnnotation1({@NestedAnnotation2("2-1"), @NestedAnnotation2("2-2")})
|
||||
class WithNestedAssociatedExplicitContainers {}
|
||||
|
||||
class WithNestedAssociatedExplicitContainersSubclass extends WithNestedAssociatedExplicitContainers {}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import java
|
||||
|
||||
class RelevantAnnotatable extends Annotatable {
|
||||
RelevantAnnotatable() {
|
||||
getCompilationUnit().hasName("Annotatable") and getCompilationUnit().fromSource()
|
||||
}
|
||||
}
|
||||
|
||||
query Annotation declaredAnnotation(RelevantAnnotatable a) { result = a.getADeclaredAnnotation() }
|
||||
|
||||
/** Note: Only has the annotations as result which are not also considered _declared_. */
|
||||
query Annotation annotationAdditional(RelevantAnnotatable a) {
|
||||
result = a.getAnAnnotation() and not result = a.getADeclaredAnnotation()
|
||||
}
|
||||
|
||||
/** Sanity check to verify that `getADeclaredAnnotation()` is a subset of `getAnAnnotation()` */
|
||||
query Annotation bugAnnotationAdditional(RelevantAnnotatable a) {
|
||||
result = a.getADeclaredAnnotation() and not result = a.getAnAnnotation()
|
||||
}
|
||||
|
||||
/** Note: Only has the annotations as result which are not part of `getAnAnnotation()`. */
|
||||
query Annotation associatedAnnotationAdditional(RelevantAnnotatable a) {
|
||||
result = a.getAnAssociatedAnnotation() and not result = a.getAnAnnotation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Covers all results of `getAnAssociatedAnnotation()` which are not also a result of `getAnAnnotation()`.
|
||||
* This should only be the case for a base class using an inheritable annotation `A` and a subclass which
|
||||
* has an annotation `CA` of the container type of `A`. In that case `A` is not considered _associated_
|
||||
* and the _indirect_ annotations from `CA` are considered instead.
|
||||
*/
|
||||
query Annotation associatedAnnotationNotInherited(RelevantAnnotatable a) {
|
||||
result = a.getAnAnnotation() and not result = a.getAnAssociatedAnnotation()
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
value
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | annotationValue | AnnotationValues.class:0:0:0:0 | CustomAnnotation |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | booleanValue | AnnotationValues.class:0:0:0:0 | false |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | byteValue | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | charValue | AnnotationValues.class:0:0:0:0 | \uffff |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | classValue | AnnotationValues.class:0:0:0:0 | AnnotationValues.class |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | doubleValue | AnnotationValues.class:0:0:0:0 | -1.0 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | enumValue | AnnotationValues.class:0:0:0:0 | DEFAULT |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | floatValue | AnnotationValues.class:0:0:0:0 | -1.0 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | intValue | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | longValue | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | shortValue | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | stringValue | AnnotationValues.class:0:0:0:0 | "\u0000" |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | annotationValue | AnnotationValues.java:54:27:54:53 | CustomAnnotation |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | booleanValue | AnnotationValues.java:49:24:49:27 | true |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | byteValue | AnnotationValues.java:43:21:43:21 | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | charValue | AnnotationValues.java:50:21:50:21 | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | classValue | AnnotationValues.java:52:22:52:39 | SingleValues.class |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | doubleValue | AnnotationValues.java:48:23:48:23 | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | enumValue | AnnotationValues.java:53:21:53:32 | CustomEnum.A |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | floatValue | AnnotationValues.java:47:22:47:22 | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | intValue | AnnotationValues.java:45:20:45:20 | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | longValue | AnnotationValues.java:46:21:46:21 | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | shortValue | AnnotationValues.java:44:22:44:22 | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | stringValue | AnnotationValues.java:51:23:51:25 | "a" |
|
||||
| AnnotationValues.java:54:27:54:53 | CustomAnnotation | value | AnnotationValues.java:54:45:54:52 | "single" |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | annotationValue | AnnotationValues.java:70:27:70:53 | CustomAnnotation |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | booleanValue | AnnotationValues.java:65:24:65:30 | BOOLEAN |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | byteValue | AnnotationValues.java:59:21:59:24 | BYTE |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | charValue | AnnotationValues.java:66:21:66:24 | CHAR |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | classValue | AnnotationValues.java:68:22:68:39 | SingleValues.class |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | doubleValue | AnnotationValues.java:64:23:64:28 | DOUBLE |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | enumValue | AnnotationValues.java:69:21:69:32 | CustomEnum.A |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | floatValue | AnnotationValues.java:63:22:63:26 | FLOAT |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | intValue | AnnotationValues.java:61:20:61:22 | INT |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | longValue | AnnotationValues.java:62:21:62:24 | LONG |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | shortValue | AnnotationValues.java:60:22:60:26 | SHORT |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | stringValue | AnnotationValues.java:67:23:67:28 | STRING |
|
||||
| AnnotationValues.java:70:27:70:53 | CustomAnnotation | value | AnnotationValues.java:70:45:70:52 | "single" |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | annotationValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | booleanValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | byteValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | charValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | classValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | doubleValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | enumValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | floatValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | intValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | longValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | shortValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | stringValues | AnnotationValues.class:0:0:0:0 | {...} |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | annotationValues | AnnotationValues.java:105:28:105:54 | {...} |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | booleanValues | AnnotationValues.java:100:25:100:28 | true |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | byteValues | AnnotationValues.java:94:22:94:22 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | charValues | AnnotationValues.java:101:22:101:24 | 'a' |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | classValues | AnnotationValues.java:103:23:103:39 | ArrayValues.class |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | doubleValues | AnnotationValues.java:99:24:99:24 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | enumValues | AnnotationValues.java:104:22:104:33 | CustomEnum.A |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | floatValues | AnnotationValues.java:98:23:98:23 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | intValues | AnnotationValues.java:96:21:96:21 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | longValues | AnnotationValues.java:97:22:97:22 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | shortValues | AnnotationValues.java:95:23:95:23 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | stringValues | AnnotationValues.java:102:24:102:26 | "a" |
|
||||
| AnnotationValues.java:105:28:105:54 | CustomAnnotation | value | AnnotationValues.java:105:46:105:53 | "single" |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | annotationValues | AnnotationValues.java:121:28:121:84 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | booleanValues | AnnotationValues.java:116:25:116:40 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | byteValues | AnnotationValues.java:110:22:110:30 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | charValues | AnnotationValues.java:117:22:117:32 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | classValues | AnnotationValues.java:119:23:119:61 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | doubleValues | AnnotationValues.java:115:24:115:34 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | enumValues | AnnotationValues.java:120:22:120:49 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | floatValues | AnnotationValues.java:114:23:114:32 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | intValues | AnnotationValues.java:112:21:112:28 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | longValues | AnnotationValues.java:113:22:113:30 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | shortValues | AnnotationValues.java:111:23:111:32 | {...} |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | stringValues | AnnotationValues.java:118:24:118:33 | {...} |
|
||||
| AnnotationValues.java:121:29:121:54 | CustomAnnotation | value | AnnotationValues.java:121:47:121:53 | "first" |
|
||||
| AnnotationValues.java:121:57:121:83 | CustomAnnotation | value | AnnotationValues.java:121:75:121:82 | "second" |
|
||||
enumConstantValue
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | enumValue | AnnotationValues.java:14:9:14:15 | DEFAULT |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | enumValue | AnnotationValues.java:15:9:15:9 | A |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | enumValue | AnnotationValues.java:15:9:15:9 | A |
|
||||
stringValue
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | stringValue | \u0000 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | stringValue | a |
|
||||
| AnnotationValues.java:54:27:54:53 | CustomAnnotation | value | single |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | stringValue | b |
|
||||
| AnnotationValues.java:70:27:70:53 | CustomAnnotation | value | single |
|
||||
| AnnotationValues.java:105:28:105:54 | CustomAnnotation | value | single |
|
||||
| AnnotationValues.java:121:29:121:54 | CustomAnnotation | value | first |
|
||||
| AnnotationValues.java:121:57:121:83 | CustomAnnotation | value | second |
|
||||
intValue
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | byteValue | -1 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | charValue | 65535 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | intValue | -1 |
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | shortValue | -1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | byteValue | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | charValue | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | intValue | 1 |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | shortValue | 1 |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | byteValue | 2 |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | charValue | 98 |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | intValue | 2 |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | shortValue | 2 |
|
||||
booleanValue
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | booleanValue | false |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | booleanValue | true |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | booleanValue | true |
|
||||
typeValue
|
||||
| AnnotationValues.java:39:5:39:17 | SingleValues | classValue | AnnotationValues.java:1:7:1:22 | AnnotationValues |
|
||||
| AnnotationValues.java:42:5:55:5 | SingleValues | classValue | AnnotationValues.java:23:16:23:27 | SingleValues |
|
||||
| AnnotationValues.java:58:5:71:5 | SingleValues | classValue | AnnotationValues.java:23:16:23:27 | SingleValues |
|
||||
arrayValue
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | annotationValues | 0 | AnnotationValues.class:0:0:0:0 | CustomAnnotation |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | booleanValues | 0 | AnnotationValues.class:0:0:0:0 | false |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | byteValues | 0 | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | charValues | 0 | AnnotationValues.class:0:0:0:0 | \uffff |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | classValues | 0 | AnnotationValues.class:0:0:0:0 | AnnotationValues.class |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | doubleValues | 0 | AnnotationValues.class:0:0:0:0 | -1.0 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | enumValues | 0 | AnnotationValues.class:0:0:0:0 | DEFAULT |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | floatValues | 0 | AnnotationValues.class:0:0:0:0 | -1.0 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | intValues | 0 | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | longValues | 0 | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | shortValues | 0 | AnnotationValues.class:0:0:0:0 | -1 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | stringValues | 0 | AnnotationValues.class:0:0:0:0 | "\u0000" |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | annotationValues | 0 | AnnotationValues.java:105:28:105:54 | CustomAnnotation |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | booleanValues | 0 | AnnotationValues.java:100:25:100:28 | true |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | byteValues | 0 | AnnotationValues.java:94:22:94:22 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | charValues | 0 | AnnotationValues.java:101:22:101:24 | 'a' |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | classValues | 0 | AnnotationValues.java:103:23:103:39 | ArrayValues.class |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | doubleValues | 0 | AnnotationValues.java:99:24:99:24 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | enumValues | 0 | AnnotationValues.java:104:22:104:33 | CustomEnum.A |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | floatValues | 0 | AnnotationValues.java:98:23:98:23 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | intValues | 0 | AnnotationValues.java:96:21:96:21 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | longValues | 0 | AnnotationValues.java:97:22:97:22 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | shortValues | 0 | AnnotationValues.java:95:23:95:23 | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | stringValues | 0 | AnnotationValues.java:102:24:102:26 | "a" |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | annotationValues | 0 | AnnotationValues.java:121:29:121:54 | CustomAnnotation |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | annotationValues | 1 | AnnotationValues.java:121:57:121:83 | CustomAnnotation |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | booleanValues | 0 | AnnotationValues.class:0:0:0:0 | false |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | booleanValues | 1 | AnnotationValues.class:0:0:0:0 | true |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | booleanValues | -1 | AnnotationValues.java:116:26:116:30 | false |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | booleanValues | -2 | AnnotationValues.java:116:33:116:39 | BOOLEAN |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | byteValues | 0 | AnnotationValues.class:0:0:0:0 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | byteValues | 1 | AnnotationValues.class:0:0:0:0 | 2 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | byteValues | -1 | AnnotationValues.java:110:23:110:23 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | byteValues | -2 | AnnotationValues.java:110:26:110:29 | BYTE |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | charValues | 0 | AnnotationValues.class:0:0:0:0 | a |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | charValues | 1 | AnnotationValues.class:0:0:0:0 | b |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | charValues | -1 | AnnotationValues.java:117:23:117:25 | 'a' |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | charValues | -2 | AnnotationValues.java:117:28:117:31 | CHAR |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | classValues | 0 | AnnotationValues.class:0:0:0:0 | SingleValues.class |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | classValues | 1 | AnnotationValues.class:0:0:0:0 | ArrayValues.class |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | classValues | -1 | AnnotationValues.java:119:24:119:41 | SingleValues.class |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | classValues | -2 | AnnotationValues.java:119:44:119:60 | ArrayValues.class |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | doubleValues | 0 | AnnotationValues.class:0:0:0:0 | 1.0 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | doubleValues | 1 | AnnotationValues.class:0:0:0:0 | 2.0 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | doubleValues | -1 | AnnotationValues.java:115:25:115:25 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | doubleValues | -2 | AnnotationValues.java:115:28:115:33 | DOUBLE |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | enumValues | 0 | AnnotationValues.class:0:0:0:0 | A |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | enumValues | 1 | AnnotationValues.class:0:0:0:0 | B |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | enumValues | -1 | AnnotationValues.java:120:23:120:34 | CustomEnum.A |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | enumValues | -2 | AnnotationValues.java:120:37:120:48 | CustomEnum.B |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | floatValues | 0 | AnnotationValues.class:0:0:0:0 | 1.0 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | floatValues | 1 | AnnotationValues.class:0:0:0:0 | 2.0 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | floatValues | -1 | AnnotationValues.java:114:24:114:24 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | floatValues | -2 | AnnotationValues.java:114:27:114:31 | FLOAT |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | intValues | 0 | AnnotationValues.class:0:0:0:0 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | intValues | 1 | AnnotationValues.class:0:0:0:0 | 2 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | intValues | -1 | AnnotationValues.java:112:22:112:22 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | intValues | -2 | AnnotationValues.java:112:25:112:27 | INT |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | longValues | 0 | AnnotationValues.class:0:0:0:0 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | longValues | 1 | AnnotationValues.class:0:0:0:0 | 2 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | longValues | -1 | AnnotationValues.java:113:23:113:23 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | longValues | -2 | AnnotationValues.java:113:26:113:29 | LONG |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | shortValues | 0 | AnnotationValues.class:0:0:0:0 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | shortValues | 1 | AnnotationValues.class:0:0:0:0 | 2 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | shortValues | -1 | AnnotationValues.java:111:24:111:24 | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | shortValues | -2 | AnnotationValues.java:111:27:111:31 | SHORT |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | stringValues | 0 | AnnotationValues.class:0:0:0:0 | "a" |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | stringValues | 1 | AnnotationValues.class:0:0:0:0 | "b" |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | stringValues | -1 | AnnotationValues.java:118:25:118:27 | "a" |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | stringValues | -2 | AnnotationValues.java:118:30:118:32 | "b" |
|
||||
enumConstantArrayValue
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | enumValues | AnnotationValues.java:14:9:14:15 | DEFAULT |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | enumValues | AnnotationValues.java:15:9:15:9 | A |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | enumValues | AnnotationValues.java:15:9:15:9 | A |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | enumValues | AnnotationValues.java:16:9:16:9 | B |
|
||||
stringArrayValue
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | stringValues | \u0000 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | stringValues | a |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | stringValues | a |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | stringValues | b |
|
||||
intArrayValue
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | byteValues | -1 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | charValues | 65535 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | intValues | -1 |
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | shortValues | -1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | byteValues | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | charValues | 97 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | intValues | 1 |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | shortValues | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | byteValues | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | byteValues | 2 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | charValues | 97 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | charValues | 98 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | intValues | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | intValues | 2 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | shortValues | 1 |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | shortValues | 2 |
|
||||
typeArrayValue
|
||||
| AnnotationValues.java:90:5:90:16 | ArrayValues | classValues | AnnotationValues.java:1:7:1:22 | AnnotationValues |
|
||||
| AnnotationValues.java:93:5:106:5 | ArrayValues | classValues | AnnotationValues.java:74:16:74:26 | ArrayValues |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | classValues | AnnotationValues.java:23:16:23:27 | SingleValues |
|
||||
| AnnotationValues.java:109:5:122:5 | ArrayValues | classValues | AnnotationValues.java:74:16:74:26 | ArrayValues |
|
|
@ -0,0 +1,37 @@
|
|||
import java
|
||||
|
||||
class RelevantAnnotation extends Annotation {
|
||||
RelevantAnnotation() {
|
||||
getCompilationUnit().hasName("AnnotationValues") and getCompilationUnit().fromSource()
|
||||
}
|
||||
}
|
||||
|
||||
query Expr value(RelevantAnnotation a, string name) { result = a.getValue(name) }
|
||||
|
||||
query EnumConstant enumConstantValue(RelevantAnnotation a, string name) {
|
||||
result = a.getEnumConstantValue(name)
|
||||
}
|
||||
|
||||
query string stringValue(RelevantAnnotation a, string name) { result = a.getStringValue(name) }
|
||||
|
||||
query int intValue(RelevantAnnotation a, string name) { result = a.getIntValue(name) }
|
||||
|
||||
query boolean booleanValue(RelevantAnnotation a, string name) { result = a.getBooleanValue(name) }
|
||||
|
||||
query Type typeValue(RelevantAnnotation a, string name) { result = a.getTypeValue(name) }
|
||||
|
||||
query Expr arrayValue(RelevantAnnotation a, string name, int index) {
|
||||
result = a.getArrayValue(name, index)
|
||||
}
|
||||
|
||||
query EnumConstant enumConstantArrayValue(RelevantAnnotation a, string name) {
|
||||
result = a.getAnEnumConstantArrayValue(name)
|
||||
}
|
||||
|
||||
query string stringArrayValue(RelevantAnnotation a, string name) {
|
||||
result = a.getAStringArrayValue(name)
|
||||
}
|
||||
|
||||
query int intArrayValue(RelevantAnnotation a, string name) { result = a.getAnIntArrayValue(name) }
|
||||
|
||||
query Type typeArrayValue(RelevantAnnotation a, string name) { result = a.getATypeArrayValue(name) }
|
|
@ -0,0 +1,13 @@
|
|||
annotationType
|
||||
| AnnotationType.java:11:16:11:34 | InheritedAnnotation | inherited | <any-target> | CLASS |
|
||||
| AnnotationType.java:14:16:14:35 | DocumentedAnnotation | documented | <any-target> | CLASS |
|
||||
| AnnotationType.java:16:16:16:34 | ContainerAnnotation | | <any-target> | CLASS |
|
||||
| AnnotationType.java:21:16:21:35 | RepeatableAnnotation | repeatable | <any-target> | CLASS |
|
||||
| AnnotationType.java:25:16:25:26 | EmptyTarget | | | CLASS |
|
||||
| AnnotationType.java:28:16:28:27 | SingleTarget | | ANNOTATION_TYPE | CLASS |
|
||||
| AnnotationType.java:43:16:43:25 | AllTargets | | ANNOTATION_TYPE,CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,MODULE,PACKAGE,PARAMETER,RECORD_COMPONENT,TYPE_PARAMETER,TYPE_USE | CLASS |
|
||||
| AnnotationType.java:47:16:47:29 | ClassRetention | | <any-target> | CLASS |
|
||||
| AnnotationType.java:50:16:50:31 | RuntimeRetention | | <any-target> | RUNTIME |
|
||||
| AnnotationType.java:53:16:53:30 | SourceRetention | | <any-target> | SOURCE |
|
||||
containingAnnotationType
|
||||
| AnnotationType.java:21:16:21:35 | RepeatableAnnotation | AnnotationType.java:16:16:16:34 | ContainerAnnotation |
|
|
@ -0,0 +1,54 @@
|
|||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
class AnnotationType {
|
||||
@Inherited
|
||||
@interface InheritedAnnotation {}
|
||||
|
||||
@Documented
|
||||
@interface DocumentedAnnotation {}
|
||||
|
||||
@interface ContainerAnnotation {
|
||||
RepeatableAnnotation[] value();
|
||||
}
|
||||
|
||||
@Repeatable(ContainerAnnotation.class)
|
||||
@interface RepeatableAnnotation {}
|
||||
|
||||
|
||||
@Target({})
|
||||
@interface EmptyTarget {}
|
||||
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@interface SingleTarget {}
|
||||
|
||||
@Target({
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.FIELD,
|
||||
ElementType.LOCAL_VARIABLE,
|
||||
ElementType.METHOD,
|
||||
ElementType.MODULE,
|
||||
ElementType.PACKAGE,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.RECORD_COMPONENT,
|
||||
ElementType.TYPE_PARAMETER,
|
||||
ElementType.TYPE_USE
|
||||
})
|
||||
@interface AllTargets {}
|
||||
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@interface ClassRetention {}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface RuntimeRetention {}
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface SourceRetention {}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import java
|
||||
|
||||
class RelevantAnnotationType extends AnnotationType {
|
||||
RelevantAnnotationType() { getCompilationUnit().hasName("AnnotationType") }
|
||||
}
|
||||
|
||||
query predicate annotationType(
|
||||
RelevantAnnotationType t, string flagsString, string targets, string retentionPolicy
|
||||
) {
|
||||
flagsString =
|
||||
concat(string s |
|
||||
t.isInherited() and s = "inherited"
|
||||
or
|
||||
t.isDocumented() and s = "documented"
|
||||
or
|
||||
t.isRepeatable() and s = "repeatable"
|
||||
|
|
||||
s, "," order by s
|
||||
) and
|
||||
(
|
||||
// Workaround to test if no explicit @Target is specified; in that case any string except
|
||||
// TYPE_USE, which represents type contexts, is considered a target because it might be
|
||||
// added to ElementType in a future JDK version
|
||||
if t.isATargetType("<any-target>")
|
||||
then
|
||||
if t.isATargetType("TYPE_USE")
|
||||
then targets = "BUG: Includes TYPE_USE"
|
||||
else targets = "<any-target>"
|
||||
else
|
||||
targets =
|
||||
concat(string s |
|
||||
exists(EnumConstant elementType |
|
||||
elementType.getDeclaringType().hasQualifiedName("java.lang.annotation", "ElementType") and
|
||||
s = elementType.getName() and
|
||||
t.isATargetType(s)
|
||||
)
|
||||
|
|
||||
s, "," order by s
|
||||
)
|
||||
) and
|
||||
retentionPolicy = t.getRetentionPolicy()
|
||||
}
|
||||
|
||||
query AnnotationType containingAnnotationType(RelevantAnnotationType t) {
|
||||
result = t.getContainingAnnotationType()
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
class AnnotationValues {
|
||||
private static final byte BYTE = 2;
|
||||
private static final short SHORT = 2;
|
||||
private static final int INT = 2;
|
||||
private static final long LONG = 2;
|
||||
private static final float FLOAT = 2;
|
||||
private static final double DOUBLE = 2;
|
||||
private static final boolean BOOLEAN = true;
|
||||
private static final char CHAR = 'b';
|
||||
|
||||
private static final String STRING = "b";
|
||||
|
||||
enum CustomEnum {
|
||||
DEFAULT,
|
||||
A,
|
||||
B
|
||||
}
|
||||
|
||||
@interface CustomAnnotation {
|
||||
String value();
|
||||
}
|
||||
|
||||
@interface SingleValues {
|
||||
byte byteValue() default -1;
|
||||
short shortValue() default -1;
|
||||
int intValue() default -1;
|
||||
long longValue() default -1;
|
||||
float floatValue() default -1;
|
||||
double doubleValue() default -1;
|
||||
boolean booleanValue() default false;
|
||||
char charValue() default '\uFFFF';
|
||||
|
||||
String stringValue() default "\0";
|
||||
Class<?> classValue() default AnnotationValues.class;
|
||||
CustomEnum enumValue() default CustomEnum.DEFAULT;
|
||||
CustomAnnotation annotationValue() default @CustomAnnotation("default");
|
||||
}
|
||||
|
||||
@SingleValues
|
||||
private int singleValuesDefault;
|
||||
|
||||
@SingleValues(
|
||||
byteValue = 1,
|
||||
shortValue = 1,
|
||||
intValue = 1,
|
||||
longValue = 1,
|
||||
floatValue = 1,
|
||||
doubleValue = 1,
|
||||
booleanValue = true,
|
||||
charValue = 1,
|
||||
stringValue = "a",
|
||||
classValue = SingleValues.class,
|
||||
enumValue = CustomEnum.A,
|
||||
annotationValue = @CustomAnnotation("single")
|
||||
)
|
||||
private int singleValues;
|
||||
|
||||
@SingleValues(
|
||||
byteValue = BYTE,
|
||||
shortValue = SHORT,
|
||||
intValue = INT,
|
||||
longValue = LONG,
|
||||
floatValue = FLOAT,
|
||||
doubleValue = DOUBLE,
|
||||
booleanValue = BOOLEAN,
|
||||
charValue = CHAR,
|
||||
stringValue = STRING,
|
||||
classValue = SingleValues.class,
|
||||
enumValue = CustomEnum.A,
|
||||
annotationValue = @CustomAnnotation("single")
|
||||
)
|
||||
private int singleValuesConstants;
|
||||
|
||||
@interface ArrayValues {
|
||||
byte[] byteValues() default -1;
|
||||
short[] shortValues() default -1;
|
||||
int[] intValues() default -1;
|
||||
long[] longValues() default -1;
|
||||
float[] floatValues() default -1;
|
||||
double[] doubleValues() default -1;
|
||||
boolean[] booleanValues() default false;
|
||||
char[] charValues() default '\uFFFF';
|
||||
|
||||
String[] stringValues() default "\0";
|
||||
Class<?>[] classValues() default AnnotationValues.class;
|
||||
CustomEnum[] enumValues() default CustomEnum.DEFAULT;
|
||||
CustomAnnotation[] annotationValues() default @CustomAnnotation("default");
|
||||
}
|
||||
|
||||
@ArrayValues
|
||||
private int arrayValuesDefault;
|
||||
|
||||
@ArrayValues(
|
||||
byteValues = 1,
|
||||
shortValues = 1,
|
||||
intValues = 1,
|
||||
longValues = 1,
|
||||
floatValues = 1,
|
||||
doubleValues = 1,
|
||||
booleanValues = true,
|
||||
charValues = 'a',
|
||||
stringValues = "a",
|
||||
classValues = ArrayValues.class,
|
||||
enumValues = CustomEnum.A,
|
||||
annotationValues = @CustomAnnotation("single")
|
||||
)
|
||||
private int arrayValuesSingleExpr;
|
||||
|
||||
@ArrayValues(
|
||||
byteValues = {1, BYTE},
|
||||
shortValues = {1, SHORT},
|
||||
intValues = {1, INT},
|
||||
longValues = {1, LONG},
|
||||
floatValues = {1, FLOAT},
|
||||
doubleValues = {1, DOUBLE},
|
||||
booleanValues = {false, BOOLEAN},
|
||||
charValues = {'a', CHAR},
|
||||
stringValues = {"a", "b"},
|
||||
classValues = {SingleValues.class, ArrayValues.class},
|
||||
enumValues = {CustomEnum.A, CustomEnum.B},
|
||||
annotationValues = {@CustomAnnotation("first"), @CustomAnnotation("second")}
|
||||
)
|
||||
private int arrayValues;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
//semmle-extractor-options: --javac-args -source 16 -target 16
|
|
@ -984,7 +984,7 @@ module TaintTracking {
|
|||
*
|
||||
* `<contains>` is one of: `contains`, `has`, `hasOwnProperty`
|
||||
*
|
||||
* Note that the `includes` method is covered by `StringInclusionSanitizer`.
|
||||
* Note that the `includes` method is covered by `MembershipTestSanitizer`.
|
||||
*/
|
||||
class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode,
|
||||
DataFlow::MethodCallNode {
|
||||
|
@ -1171,7 +1171,7 @@ module TaintTracking {
|
|||
/**
|
||||
* A check of form `x.indexOf(y) > 0` or similar, which sanitizes `y` in the "then" branch.
|
||||
*
|
||||
* The more typical case of `x.indexOf(y) >= 0` is covered by `StringInclusionSanitizer`.
|
||||
* The more typical case of `x.indexOf(y) >= 0` is covered by `MembershipTestSanitizer`.
|
||||
*/
|
||||
class PositiveIndexOfSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
MethodCallExpr indexOf;
|
||||
|
|
|
@ -18,32 +18,44 @@ import javascript
|
|||
* A call that checks a property of some file.
|
||||
*/
|
||||
class FileCheck extends DataFlow::CallNode {
|
||||
string member;
|
||||
|
||||
FileCheck() {
|
||||
this =
|
||||
NodeJSLib::FS::moduleMember([
|
||||
"open", "openSync", "exists", "existsSync", "stat", "statSync", "lstat", "lstatSync",
|
||||
"fstat", "fstatSync", "access", "accessSync"
|
||||
]).getACall()
|
||||
member =
|
||||
[
|
||||
"open", "openSync", "exists", "existsSync", "stat", "statSync", "lstat", "lstatSync",
|
||||
"fstat", "fstatSync", "access", "accessSync"
|
||||
] and
|
||||
this = NodeJSLib::FS::moduleMember(member).getACall()
|
||||
}
|
||||
|
||||
DataFlow::Node getPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
/** Holds if this call is a simple existence check for a file. */
|
||||
predicate isExistsCheck() { member = ["exists", "existsSync"] }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that modifies or otherwise interacts with a file.
|
||||
*/
|
||||
class FileUse extends DataFlow::CallNode {
|
||||
string member;
|
||||
|
||||
FileUse() {
|
||||
this =
|
||||
NodeJSLib::FS::moduleMember([
|
||||
// these are the six methods that accept file paths and file descriptors
|
||||
"readFile", "readFileSync", "writeFile", "writeFileSync", "appendFile", "appendFileSync",
|
||||
// don't use "open" after e.g. "access"
|
||||
"open", "openSync"
|
||||
]).getACall()
|
||||
member =
|
||||
[
|
||||
// these are the six methods that accept file paths and file descriptors
|
||||
"readFile", "readFileSync", "writeFile", "writeFileSync", "appendFile", "appendFileSync",
|
||||
// don't use "open" after e.g. "access"
|
||||
"open", "openSync"
|
||||
] and
|
||||
this = NodeJSLib::FS::moduleMember(member).getACall()
|
||||
}
|
||||
|
||||
DataFlow::Node getPathArgument() { result = this.getArgument(0) }
|
||||
|
||||
/** Holds if this call reads from a file. */
|
||||
predicate isFileRead() { member = ["readFile", "readFileSync"] }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,5 +113,6 @@ from FileCheck check, FileUse use
|
|||
where
|
||||
checkAndUseOnSame(check, use) and
|
||||
useAfterCheck(check, use) and
|
||||
not (check.isExistsCheck() and use.isFileRead()) and // a read after an exists check is fine
|
||||
not getAFileHandle(DataFlow::TypeTracker::end()).flowsTo(use.getPathArgument())
|
||||
select use, "The file may have changed since it $@.", check, "was checked"
|
||||
|
|
|
@ -36,3 +36,8 @@ fs.access("myfile", (err) => {
|
|||
// ....
|
||||
});
|
||||
});
|
||||
|
||||
const filePath3 = createFile();
|
||||
if (fs.existsSync(filePath3)) {
|
||||
fs.readFileSync(filePath3); // OK - a read after an existence check is OK
|
||||
}
|
||||
|
|
|
@ -26,6 +26,14 @@ def codeql_workspace(repository_name = "codeql"):
|
|||
sha256 = sha256,
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "fishhook",
|
||||
url = "https://github.com/facebook/fishhook/archive/aadc161ac3b80db07a9908851839a17ba63a9eb1.zip",
|
||||
build_file = "@%s//swift/tools/fishhook:BUILD.fishhook.bazel" % repository_name,
|
||||
strip_prefix = "fishhook-aadc161ac3b80db07a9908851839a17ba63a9eb1",
|
||||
sha256 = "9f2cdee6dcc2039d4c47d25ab5141fe0678ce6ed27ef482cab17fe9fa38a30ce",
|
||||
)
|
||||
|
||||
maybe(
|
||||
repo_rule = http_archive,
|
||||
name = "rules_pkg",
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
# The Python libraries
|
||||
|
||||
The Python libraries are a collection of libraries for analysing Python code.
|
||||
Everythng can be imported by importing `python.qll`.
|
||||
|
||||
## The analysis layers
|
||||
|
||||
The analysis is built up in layers. the stack looks like this:
|
||||
|
||||
- AST (coms from the extractor)
|
||||
- Control flow graph (CFG) (built by the extractor)
|
||||
- SSA
|
||||
- Call graph
|
||||
- Data flow
|
||||
|
||||
## Avoiding non-monotonic recursion
|
||||
|
||||
Given the many interactivg layers, it is imprtant to decie which predicates are allowed to be mutually recursive in order to avoid non-monotonic recursion when negation is used to express the predicates.
|
||||
As an example, we have defined local source as those whcih do not receive local flow. This means that the local flow relation is not allowed to be recursive with anything depending on local sources.
|
||||
|
||||
Some particular reatrictions to keep in mind:
|
||||
|
||||
- Typetracking needs to use a local flow step not including summaries
|
||||
- Typetracking needs to use a call graph not including summaries
|
|
@ -380,6 +380,21 @@ module API {
|
|||
not m.matches("%.%")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an import of module `m` exists.
|
||||
*
|
||||
* This is determined without referring to `Node`,
|
||||
* allowing this predicate to be used in a negative
|
||||
* context when constructing new nodes.
|
||||
*/
|
||||
predicate moduleImportExists(string m) {
|
||||
Impl::isImported(m) and
|
||||
// restrict `moduleImport` so it will never give results for a dotted name. Note
|
||||
// that we cannot move this logic to the `MkModuleImport` construction, since we
|
||||
// need the intermediate API graph nodes for the prefixes in `import foo.bar.baz`.
|
||||
not m.matches("%.%")
|
||||
}
|
||||
|
||||
/** Gets a node corresponding to the built-in with the given name, if any. */
|
||||
Node builtin(string n) { result = moduleImport("builtins").getMember(n) }
|
||||
|
||||
|
@ -605,14 +620,38 @@ module API {
|
|||
*
|
||||
* Ignores relative imports, such as `from ..foo.bar import baz`.
|
||||
*/
|
||||
private predicate imports(DataFlow::Node imp, string name) {
|
||||
private predicate imports(DataFlow::CfgNode imp, string name) {
|
||||
exists(PY::ImportExprNode iexpr |
|
||||
imp.asCfgNode() = iexpr and
|
||||
imp.getNode() = iexpr and
|
||||
not iexpr.getNode().isRelative() and
|
||||
name = iexpr.getNode().getImportedModuleName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the module `name` is imported.
|
||||
*
|
||||
* This is determined syntactically.
|
||||
*/
|
||||
cached
|
||||
predicate isImported(string name) {
|
||||
// Ignore the following module name for Python 2, as we alias `__builtin__` to `builtins` elsewhere
|
||||
(name != "__builtin__" or PY::major_version() = 3) and
|
||||
(
|
||||
exists(PY::ImportExpr iexpr |
|
||||
not iexpr.isRelative() and
|
||||
name = iexpr.getImportedModuleName()
|
||||
)
|
||||
or
|
||||
// When we `import foo.bar.baz` we want to create API graph nodes also for the prefixes
|
||||
// `foo` and `foo.bar`:
|
||||
name = any(PY::ImportExpr e | not e.isRelative()).getAnImportedModuleName()
|
||||
)
|
||||
or
|
||||
// The `builtins` module should always be implicitly available
|
||||
name = "builtins"
|
||||
}
|
||||
|
||||
private import semmle.python.dataflow.new.internal.Builtins
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
|
||||
|
@ -631,7 +670,7 @@ module API {
|
|||
*/
|
||||
private TApiNode potential_import_star_base(PY::Scope s) {
|
||||
exists(DataFlow::Node n |
|
||||
n.asCfgNode() = ImportStar::potentialImportStarBase(s) and
|
||||
n.(DataFlow::CfgNode).getNode() = ImportStar::potentialImportStarBase(s) and
|
||||
use(result, n)
|
||||
)
|
||||
}
|
||||
|
@ -653,17 +692,17 @@ module API {
|
|||
or
|
||||
// TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
|
||||
exists(PY::Dict dict, PY::KeyValuePair item |
|
||||
dict = pred.asExpr() and
|
||||
dict = pred.(DataFlow::ExprNode).getNode().getNode() and
|
||||
dict.getItem(_) = item and
|
||||
lbl = Label::member(item.getKey().(PY::StrConst).getS()) and
|
||||
rhs.asExpr() = item.getValue()
|
||||
rhs.(DataFlow::ExprNode).getNode().getNode() = item.getValue()
|
||||
)
|
||||
or
|
||||
exists(PY::CallableExpr fn | fn = pred.asExpr() |
|
||||
exists(PY::CallableExpr fn | fn = pred.(DataFlow::ExprNode).getNode().getNode() |
|
||||
not fn.getInnerScope().isAsync() and
|
||||
lbl = Label::return() and
|
||||
exists(PY::Return ret |
|
||||
rhs.asExpr() = ret.getValue() and
|
||||
rhs.(DataFlow::ExprNode).getNode().getNode() = ret.getValue() and
|
||||
ret.getScope() = fn.getInnerScope()
|
||||
)
|
||||
)
|
||||
|
@ -716,9 +755,9 @@ module API {
|
|||
// "benign" and let subclasses edges flow through anyway.
|
||||
// see example in https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46
|
||||
(
|
||||
ref.asExpr() = clsExpr
|
||||
ref.(DataFlow::ExprNode).getNode().getNode() = clsExpr
|
||||
or
|
||||
ref.asExpr() = clsExpr.getADecoratorCall()
|
||||
ref.(DataFlow::ExprNode).getNode().getNode() = clsExpr.getADecoratorCall()
|
||||
)
|
||||
)
|
||||
or
|
||||
|
@ -731,7 +770,7 @@ module API {
|
|||
)
|
||||
or
|
||||
exists(DataFlow::Node def, PY::CallableExpr fn |
|
||||
rhs(base, def) and fn = trackDefNode(def).asExpr()
|
||||
rhs(base, def) and fn = trackDefNode(def).(DataFlow::ExprNode).getNode().getNode()
|
||||
|
|
||||
exists(int i, int offset |
|
||||
if exists(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
|
||||
|
@ -739,18 +778,19 @@ module API {
|
|||
else offset = 0
|
||||
|
|
||||
lbl = Label::parameter(i - offset) and
|
||||
ref.asExpr() = fn.getInnerScope().getArg(i)
|
||||
ref.(DataFlow::ExprNode).getNode().getNode() = fn.getInnerScope().getArg(i)
|
||||
)
|
||||
or
|
||||
exists(string name, PY::Parameter param |
|
||||
lbl = Label::keywordParameter(name) and
|
||||
param = fn.getInnerScope().getArgByName(name) and
|
||||
not param.isSelf() and
|
||||
ref.asExpr() = param
|
||||
ref.(DataFlow::ExprNode).getNode().getNode() = param
|
||||
)
|
||||
or
|
||||
lbl = Label::selfParameter() and
|
||||
ref.asExpr() = any(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
|
||||
ref.(DataFlow::ExprNode).getNode().getNode() =
|
||||
any(PY::Parameter p | p = fn.getInnerScope().getAnArg() and p.isSelf())
|
||||
)
|
||||
or
|
||||
// Built-ins, treated as members of the module `builtins`
|
||||
|
@ -762,7 +802,7 @@ module API {
|
|||
base = potential_import_star_base(s) and
|
||||
lbl =
|
||||
Label::member(any(string name |
|
||||
ImportStar::namePossiblyDefinedInImportStar(ref.asCfgNode(), name, s)
|
||||
ImportStar::namePossiblyDefinedInImportStar(ref.(DataFlow::CfgNode).getNode(), name, s)
|
||||
))
|
||||
)
|
||||
or
|
||||
|
@ -854,7 +894,7 @@ module API {
|
|||
DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) {
|
||||
Stages::TypeTracking::ref() and
|
||||
result = trackUseNode(src, DataFlow::TypeTracker::end()) and
|
||||
not result instanceof DataFlow::ModuleVariableNode
|
||||
result instanceof DataFlow::ExprNode
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1044,7 +1084,7 @@ module API {
|
|||
ApiLabel memberFromRef(DataFlow::AttrRef ref) {
|
||||
result = member(ref.getAttributeName())
|
||||
or
|
||||
not exists(ref.getAttributeName()) and
|
||||
ref.unknownAttribute() and
|
||||
result = unknownMember()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
/** Provides classes and predicates for defining flow summaries. */
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.frameworks.data.ModelsAsData
|
||||
private import semmle.python.ApiGraphs
|
||||
private import internal.FlowSummaryImpl as Impl
|
||||
private import internal.DataFlowUtil
|
||||
private import internal.DataFlowPrivate
|
||||
|
||||
// import all instances below
|
||||
private module Summaries {
|
||||
private import semmle.python.Frameworks
|
||||
}
|
||||
|
||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
||||
|
||||
/** Provides predicates for constructing summary components. */
|
||||
module SummaryComponent {
|
||||
private import Impl::Public::SummaryComponent as SC
|
||||
|
||||
predicate parameter = SC::parameter/1;
|
||||
|
||||
predicate argument = SC::argument/1;
|
||||
|
||||
predicate content = SC::content/1;
|
||||
|
||||
/** Gets a summary component that represents a list element. */
|
||||
SummaryComponent listElement() { result = content(any(ListElementContent c)) }
|
||||
|
||||
/** Gets a summary component that represents the return value of a call. */
|
||||
SummaryComponent return() { result = SC::return(any(ReturnKind rk)) }
|
||||
}
|
||||
|
||||
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
|
||||
|
||||
/** Provides predicates for constructing stacks of summary components. */
|
||||
module SummaryComponentStack {
|
||||
private import Impl::Public::SummaryComponentStack as SCS
|
||||
|
||||
predicate singleton = SCS::singleton/1;
|
||||
|
||||
predicate push = SCS::push/2;
|
||||
|
||||
predicate argument = SCS::argument/1;
|
||||
|
||||
/** Gets a singleton stack representing the return value of a call. */
|
||||
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
|
||||
}
|
||||
|
||||
/** A callable with a flow summary, identified by a unique string. */
|
||||
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
|
||||
bindingset[this]
|
||||
SummarizedCallable() { any() }
|
||||
|
||||
/**
|
||||
* Same as
|
||||
*
|
||||
* ```ql
|
||||
* propagatesFlow(
|
||||
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* but uses an external (string) representation of the input and output stacks.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
|
||||
}
|
||||
|
||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
||||
// // This gives access to getNodeFromPath, which is not constrained to `CallNode`s
|
||||
// // as `resolvedSummaryBase` is.
|
||||
// private import semmle.python.frameworks.data.internal.ApiGraphModels as AGM
|
||||
//
|
||||
// private class SummarizedCallableFromModel extends SummarizedCallable {
|
||||
// string package;
|
||||
// string type;
|
||||
// string path;
|
||||
// SummarizedCallableFromModel() {
|
||||
// ModelOutput::relevantSummaryModel(package, type, path, _, _, _) and
|
||||
// this = package + ";" + type + ";" + path
|
||||
// }
|
||||
// override CallCfgNode getACall() {
|
||||
// exists(API::CallNode base |
|
||||
// ModelOutput::resolvedSummaryBase(package, type, path, base) and
|
||||
// result = base.getACall()
|
||||
// )
|
||||
// }
|
||||
// override ArgumentNode getACallback() {
|
||||
// exists(API::Node base |
|
||||
// base = AGM::getNodeFromPath(package, type, path) and
|
||||
// result = base.getAValueReachableFromSource()
|
||||
// )
|
||||
// }
|
||||
// override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
// exists(string kind |
|
||||
// ModelOutput::relevantSummaryModel(package, type, path, input, output, kind)
|
||||
// |
|
||||
// kind = "value" and
|
||||
// preservesValue = true
|
||||
// or
|
||||
// kind = "taint" and
|
||||
// preservesValue = false
|
||||
// )
|
||||
// }
|
||||
// }
|
|
@ -40,7 +40,7 @@ abstract class AttrRef extends Node {
|
|||
or
|
||||
exists(LocalSourceNode nodeFrom |
|
||||
nodeFrom.flowsTo(this.getAttributeNameExpr()) and
|
||||
attrName = nodeFrom.asExpr().(StrConst).getText()
|
||||
attrName = nodeFrom.(CfgNode).getNode().getNode().(StrConst).getText()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,9 @@ abstract class AttrRef extends Node {
|
|||
* better results.
|
||||
*/
|
||||
abstract string getAttributeName();
|
||||
|
||||
/** Holds if a name could not be determined for this attribute. */
|
||||
predicate unknownAttribute() { not exists(this.getAttributeName()) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +178,7 @@ private class SetAttrCallAsAttrWrite extends AttrWrite, CfgNode {
|
|||
override ExprNode getAttributeNameExpr() { result.asCfgNode() = node.getName() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result = this.getAttributeNameExpr().asExpr().(StrConst).getText()
|
||||
result = this.getAttributeNameExpr().(CfgNode).getNode().getNode().(StrConst).getText()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +254,7 @@ private class GetAttrCallAsAttrRead extends AttrRead, CfgNode {
|
|||
override ExprNode getAttributeNameExpr() { result.asCfgNode() = node.getName() }
|
||||
|
||||
override string getAttributeName() {
|
||||
result = this.getAttributeNameExpr().asExpr().(StrConst).getText()
|
||||
result = this.getAttributeNameExpr().(CfgNode).getNode().getNode().(StrConst).getText()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,9 +60,9 @@ module Builtins {
|
|||
* Currently this is an over-approximation, and may not account for things like overwriting a
|
||||
* built-in with a different value.
|
||||
*/
|
||||
DataFlow::Node likelyBuiltin(string name) {
|
||||
DataFlow::CfgNode likelyBuiltin(string name) {
|
||||
exists(Module m |
|
||||
result.asCfgNode() =
|
||||
result.getNode() =
|
||||
any(NameNode n |
|
||||
possible_builtin_accessed_in_module(n, name, m) and
|
||||
not possible_builtin_defined_in_module(name, m)
|
||||
|
|
|
@ -7,15 +7,22 @@
|
|||
private import python
|
||||
private import DataFlowPublic
|
||||
private import semmle.python.SpecialMethods
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition extends int {
|
||||
ParameterPosition() { exists(any(DataFlowCallable c).getParameter(this)) }
|
||||
|
||||
/** Holds if this position represents a positional parameter at position `pos`. */
|
||||
predicate isPositional(int pos) { this = pos } // with the current representation, all parameters are positional
|
||||
}
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition extends int {
|
||||
ArgumentPosition() { exists(any(DataFlowCall c).getArg(this)) }
|
||||
ArgumentPosition() { this in [-2, -1] or exists(any(Call c).getArg(this)) }
|
||||
|
||||
/** Holds if this position represents a positional argument at position `pos`. */
|
||||
predicate isPositional(int pos) { this = pos } // with the current representation, all arguments are positional
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
|
@ -96,7 +103,7 @@ module ArgumentPassing {
|
|||
* Used to limit the size of predicates.
|
||||
*/
|
||||
predicate connects(CallNode call, CallableValue callable) {
|
||||
exists(DataFlowCall c |
|
||||
exists(NormalCall c |
|
||||
call = c.getNode() and
|
||||
callable = c.getCallable().getCallableValue()
|
||||
)
|
||||
|
@ -268,6 +275,18 @@ module ArgumentPassing {
|
|||
|
||||
import ArgumentPassing
|
||||
|
||||
/** A callable defined in library code, identified by a unique string. */
|
||||
abstract class LibraryCallable extends string {
|
||||
bindingset[this]
|
||||
LibraryCallable() { any() }
|
||||
|
||||
/** Gets a call to this library callable. */
|
||||
abstract CallCfgNode getACall();
|
||||
|
||||
/** Gets a data-flow node, where this library callable is used as a call-back. */
|
||||
abstract ArgumentNode getACallback();
|
||||
}
|
||||
|
||||
/**
|
||||
* IPA type for DataFlowCallable.
|
||||
*
|
||||
|
@ -282,27 +301,33 @@ newtype TDataFlowCallable =
|
|||
callable instanceof ClassValue
|
||||
} or
|
||||
TLambda(Function lambda) { lambda.isLambda() } or
|
||||
TModule(Module m)
|
||||
TModule(Module m) or
|
||||
TLibraryCallable(LibraryCallable callable)
|
||||
|
||||
/** A callable. */
|
||||
abstract class DataFlowCallable extends TDataFlowCallable {
|
||||
class DataFlowCallable extends TDataFlowCallable {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
string toString() { result = "DataFlowCallable" }
|
||||
|
||||
/** Gets a call to this callable. */
|
||||
abstract CallNode getACall();
|
||||
CallNode getACall() { none() }
|
||||
|
||||
/** Gets the scope of this callable */
|
||||
abstract Scope getScope();
|
||||
Scope getScope() { none() }
|
||||
|
||||
/** Gets the specified parameter of this callable */
|
||||
abstract NameNode getParameter(int n);
|
||||
NameNode getParameter(int n) { none() }
|
||||
|
||||
/** Gets the name of this callable. */
|
||||
abstract string getName();
|
||||
string getName() { none() }
|
||||
|
||||
/** Gets a callable value for this callable, if one exists. */
|
||||
abstract CallableValue getCallableValue();
|
||||
/** Gets a callable value for this callable, if any. */
|
||||
CallableValue getCallableValue() { none() }
|
||||
|
||||
/** Gets the underlying library callable, if any. */
|
||||
LibraryCallable asLibraryCallable() { this = TLibraryCallable(result) }
|
||||
|
||||
Location getLocation() { none() }
|
||||
}
|
||||
|
||||
/** A class representing a callable value. */
|
||||
|
@ -343,6 +368,8 @@ class DataFlowLambda extends DataFlowCallable, TLambda {
|
|||
override FunctionValue getCallableValue() {
|
||||
result.getOrigin().getNode() = lambda.getDefinition()
|
||||
}
|
||||
|
||||
Expr getDefinition() { result = lambda.getDefinition() }
|
||||
}
|
||||
|
||||
/** A class representing the scope in which a `ModuleVariableNode` appears. */
|
||||
|
@ -364,6 +391,27 @@ class DataFlowModuleScope extends DataFlowCallable, TModule {
|
|||
override CallableValue getCallableValue() { none() }
|
||||
}
|
||||
|
||||
class LibraryCallableValue extends DataFlowCallable, TLibraryCallable {
|
||||
LibraryCallable callable;
|
||||
|
||||
LibraryCallableValue() { this = TLibraryCallable(callable) }
|
||||
|
||||
override string toString() { result = callable.toString() }
|
||||
|
||||
override CallNode getACall() { result = callable.getACall().getNode() }
|
||||
|
||||
/** Gets a data-flow node, where this library callable is used as a call-back. */
|
||||
ArgumentNode getACallback() { result = callable.getACallback() }
|
||||
|
||||
override Scope getScope() { none() }
|
||||
|
||||
override NameNode getParameter(int n) { none() }
|
||||
|
||||
override string getName() { result = callable }
|
||||
|
||||
override LibraryCallable asLibraryCallable() { result = callable }
|
||||
}
|
||||
|
||||
/**
|
||||
* IPA type for DataFlowCall.
|
||||
*
|
||||
|
@ -379,90 +427,140 @@ class DataFlowModuleScope extends DataFlowCallable, TModule {
|
|||
* TODO: Add `TClassMethodCall` mapping `cls` appropriately.
|
||||
*/
|
||||
newtype TDataFlowCall =
|
||||
TFunctionCall(CallNode call) { call = any(FunctionValue f).getAFunctionCall() } or
|
||||
/** Bound methods need to make room for the explicit self parameter */
|
||||
TMethodCall(CallNode call) { call = any(FunctionValue f).getAMethodCall() } or
|
||||
TClassCall(CallNode call) { call = any(ClassValue c | not c.isAbsent()).getACall() } or
|
||||
TSpecialCall(SpecialMethodCallNode special)
|
||||
/**
|
||||
* Includes function calls, method calls, class calls and library calls.
|
||||
* All these will be associated with a `CallNode`.
|
||||
*/
|
||||
TNormalCall(CallNode call) or
|
||||
/**
|
||||
* Includes calls to special methods.
|
||||
* These will be associated with a `SpecialMethodCallNode`.
|
||||
*/
|
||||
TSpecialCall(SpecialMethodCallNode special) or
|
||||
/** A synthesized call inside a summarized callable */
|
||||
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
/** A call. */
|
||||
/** A call found in the program source (as opposed to a synthesised summary call). */
|
||||
class TExtractedDataFlowCall = TSpecialCall or TNormalCall;
|
||||
|
||||
/** A call that is taken into account by the global data flow computation. */
|
||||
abstract class DataFlowCall extends TDataFlowCall {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Get the callable to which this call goes. */
|
||||
/** Get the callable to which this call goes, if such exists. */
|
||||
abstract DataFlowCallable getCallable();
|
||||
|
||||
/**
|
||||
* Gets the argument to this call that will be sent
|
||||
* to the `n`th parameter of the callable.
|
||||
* to the `n`th parameter of the callable, if any.
|
||||
*/
|
||||
abstract Node getArg(int n);
|
||||
|
||||
/** Get the control flow node representing this call. */
|
||||
/** Get the control flow node representing this call, if any. */
|
||||
abstract ControlFlowNode getNode();
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
|
||||
/** Gets the location of this dataflow call. */
|
||||
Location getLocation() { result = this.getNode().getLocation() }
|
||||
abstract Location getLocation();
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A call found in the program source (as opposed to a synthesised call). */
|
||||
abstract class ExtractedDataFlowCall extends DataFlowCall, TExtractedDataFlowCall {
|
||||
final override Location getLocation() { result = this.getNode().getLocation() }
|
||||
|
||||
abstract override DataFlowCallable getCallable();
|
||||
|
||||
abstract override Node getArg(int n);
|
||||
|
||||
abstract override ControlFlowNode getNode();
|
||||
}
|
||||
|
||||
/** A call associated with a `CallNode`. */
|
||||
class NormalCall extends ExtractedDataFlowCall, TNormalCall {
|
||||
CallNode call;
|
||||
|
||||
NormalCall() { this = TNormalCall(call) }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
abstract override Node getArg(int n);
|
||||
|
||||
override CallNode getNode() { result = call }
|
||||
|
||||
abstract override DataFlowCallable getCallable();
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a function/lambda.
|
||||
* A call to a function.
|
||||
* This excludes calls to bound methods, classes, and special methods.
|
||||
* Bound method calls and class calls insert an argument for the explicit
|
||||
* `self` parameter, and special method calls have special argument passing.
|
||||
*/
|
||||
class FunctionCall extends DataFlowCall, TFunctionCall {
|
||||
CallNode call;
|
||||
DataFlowCallable callable;
|
||||
class FunctionCall extends NormalCall {
|
||||
DataFlowCallableValue callable;
|
||||
|
||||
FunctionCall() {
|
||||
this = TFunctionCall(call) and
|
||||
call = any(FunctionValue f).getAFunctionCall() and
|
||||
call = callable.getACall()
|
||||
}
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
override Node getArg(int n) { result = getArg(call, TNoShift(), callable.getCallableValue(), n) }
|
||||
|
||||
override DataFlowCallable getCallable() { result = callable }
|
||||
}
|
||||
|
||||
/** A call to a lambda. */
|
||||
class LambdaCall extends NormalCall {
|
||||
DataFlowLambda callable;
|
||||
|
||||
LambdaCall() {
|
||||
call = callable.getACall() and
|
||||
callable = TLambda(any(Function f))
|
||||
}
|
||||
|
||||
override Node getArg(int n) { result = getArg(call, TNoShift(), callable.getCallableValue(), n) }
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getCallable() { result = callable }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a call to a bound method call.
|
||||
* The node representing the instance is inserted as argument to the `self` parameter.
|
||||
*/
|
||||
class MethodCall extends DataFlowCall, TMethodCall {
|
||||
CallNode call;
|
||||
class MethodCall extends NormalCall {
|
||||
FunctionValue bm;
|
||||
|
||||
MethodCall() {
|
||||
this = TMethodCall(call) and
|
||||
call = bm.getACall()
|
||||
}
|
||||
MethodCall() { call = bm.getAMethodCall() }
|
||||
|
||||
private CallableValue getCallableValue() { result = bm }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Node getArg(int n) {
|
||||
n > 0 and result = getArg(call, TShiftOneUp(), this.getCallableValue(), n)
|
||||
or
|
||||
n = 0 and result = TCfgNode(call.getFunction().(AttrNode).getObject())
|
||||
}
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getCallable() { result = TCallableValue(this.getCallableValue()) }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -471,34 +569,27 @@ class MethodCall extends DataFlowCall, TMethodCall {
|
|||
* That makes the call node be the post-update node holding the value of the object
|
||||
* after the constructor has run.
|
||||
*/
|
||||
class ClassCall extends DataFlowCall, TClassCall {
|
||||
CallNode call;
|
||||
class ClassCall extends NormalCall {
|
||||
ClassValue c;
|
||||
|
||||
ClassCall() {
|
||||
this = TClassCall(call) and
|
||||
not c.isAbsent() and
|
||||
call = c.getACall()
|
||||
}
|
||||
|
||||
private CallableValue getCallableValue() { c.getScope().getInitMethod() = result.getScope() }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Node getArg(int n) {
|
||||
n > 0 and result = getArg(call, TShiftOneUp(), this.getCallableValue(), n)
|
||||
or
|
||||
n = 0 and result = TSyntheticPreUpdateNode(TCfgNode(call))
|
||||
}
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getCallable() { result = TCallableValue(this.getCallableValue()) }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() }
|
||||
}
|
||||
|
||||
/** A call to a special method. */
|
||||
class SpecialCall extends DataFlowCall, TSpecialCall {
|
||||
class SpecialCall extends ExtractedDataFlowCall, TSpecialCall {
|
||||
SpecialMethodCallNode special;
|
||||
|
||||
SpecialCall() { this = TSpecialCall(special) }
|
||||
|
@ -518,8 +609,172 @@ class SpecialCall extends DataFlowCall, TSpecialCall {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a summarized callable, a `LibraryCallable`.
|
||||
*
|
||||
* We currently exclude all resolved calls. This means that a call to, say, `map`, which
|
||||
* is a `ClassCall`, cannot currently be given a summary.
|
||||
* We hope to lift this restriction in the future and include all potential calls to summaries
|
||||
* in this class.
|
||||
*/
|
||||
class LibraryCall extends NormalCall {
|
||||
LibraryCall() {
|
||||
// TODO: share this with `resolvedCall`
|
||||
not (
|
||||
call = any(DataFlowCallableValue cv).getACall()
|
||||
or
|
||||
call = any(DataFlowLambda l).getACall()
|
||||
or
|
||||
// TODO: this should be covered by `DataFlowCallableValue`, but a `ClassValue` is not a `CallableValue`.
|
||||
call = any(ClassValue c).getACall()
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Implement Python calling convention?
|
||||
override Node getArg(int n) { result = TCfgNode(call.getArg(n)) }
|
||||
|
||||
// We cannot refer to a `LibraryCallable` here,
|
||||
// as that could in turn refer to type tracking.
|
||||
// This call will be tied to a `LibraryCallable` via
|
||||
// `getViableCallabe` when the global data flow is assembled.
|
||||
override DataFlowCallable getCallable() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A synthesized call inside a callable with a flow summary.
|
||||
*
|
||||
* For example, in
|
||||
* ```python
|
||||
* map(lambda x: x + 1, [1, 2, 3])
|
||||
* ```
|
||||
*
|
||||
* there is a synthesized call to the lambda argument inside `map`.
|
||||
*/
|
||||
class SummaryCall extends DataFlowCall, TSummaryCall {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable c;
|
||||
private Node receiver;
|
||||
|
||||
SummaryCall() { this = TSummaryCall(c, receiver) }
|
||||
|
||||
/** Gets the data flow node that this call targets. */
|
||||
Node getReceiver() { result = receiver }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
|
||||
|
||||
override DataFlowCallable getCallable() { none() }
|
||||
|
||||
override Node getArg(int n) { none() }
|
||||
|
||||
override ControlFlowNode getNode() { none() }
|
||||
|
||||
override string toString() { result = "[summary] call to " + receiver + " in " + c }
|
||||
|
||||
override Location getLocation() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
abstract class ParameterNodeImpl extends Node {
|
||||
abstract Parameter getParameter();
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of callable `c` at the
|
||||
* (zero-based) index `i`.
|
||||
*/
|
||||
abstract predicate isParameterOf(DataFlowCallable c, int i);
|
||||
}
|
||||
|
||||
/** A parameter for a library callable with a flow summary. */
|
||||
class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable sc;
|
||||
private int pos;
|
||||
|
||||
SummaryParameterNode() { this = TSummaryParameterNode(sc, pos) }
|
||||
|
||||
override Parameter getParameter() { none() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, int i) {
|
||||
sc = c.asLibraryCallable() and i = pos
|
||||
}
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = sc }
|
||||
|
||||
override string toString() { result = "parameter " + pos + " of " + sc }
|
||||
|
||||
// Hack to return "empty location"
|
||||
override predicate hasLocationInfo(
|
||||
string file, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
file = "" and
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
/** A data-flow node used to model flow summaries. */
|
||||
class SummaryNode extends Node, TSummaryNode {
|
||||
private FlowSummaryImpl::Public::SummarizedCallable c;
|
||||
private FlowSummaryImpl::Private::SummaryNodeState state;
|
||||
|
||||
SummaryNode() { this = TSummaryNode(c, state) }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
|
||||
|
||||
override string toString() { result = "[summary] " + state + " in " + c }
|
||||
|
||||
// Hack to return "empty location"
|
||||
override predicate hasLocationInfo(
|
||||
string file, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
file = "" and
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class SummaryReturnNode extends SummaryNode, ReturnNode {
|
||||
private ReturnKind rk;
|
||||
|
||||
SummaryReturnNode() { FlowSummaryImpl::Private::summaryReturnNode(this, rk) }
|
||||
|
||||
override ReturnKind getKind() { result = rk }
|
||||
}
|
||||
|
||||
private class SummaryArgumentNode extends SummaryNode, ArgumentNode {
|
||||
SummaryArgumentNode() { FlowSummaryImpl::Private::summaryArgumentNode(_, this, _) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this, pos)
|
||||
}
|
||||
}
|
||||
|
||||
private class SummaryPostUpdateNode extends SummaryNode, PostUpdateNode {
|
||||
private Node pre;
|
||||
|
||||
SummaryPostUpdateNode() { FlowSummaryImpl::Private::summaryPostUpdateNode(this, pre) }
|
||||
|
||||
override Node getPreUpdateNode() { result = pre }
|
||||
}
|
||||
|
||||
/** Gets a viable run-time target for the call `call`. */
|
||||
DataFlowCallable viableCallable(DataFlowCall call) { result = call.getCallable() }
|
||||
DataFlowCallable viableCallable(ExtractedDataFlowCall call) {
|
||||
result = call.getCallable()
|
||||
or
|
||||
// A call to a library callable with a flow summary
|
||||
// In this situation we can not resolve the callable from the call,
|
||||
// as that would make data flow depend on type tracking.
|
||||
// Instead we reolve the call from the summary.
|
||||
exists(LibraryCallable callable |
|
||||
result = TLibraryCallable(callable) and
|
||||
call.getNode() = callable.getACall().getNode()
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TReturnKind = TNormalReturnKind()
|
||||
|
||||
|
@ -533,26 +788,51 @@ class ReturnKind extends TReturnKind {
|
|||
}
|
||||
|
||||
/** A data flow node that represents a value returned by a callable. */
|
||||
class ReturnNode extends CfgNode {
|
||||
Return ret;
|
||||
|
||||
// See `TaintTrackingImplementation::returnFlowStep`
|
||||
ReturnNode() { node = ret.getValue().getAFlowNode() }
|
||||
|
||||
abstract class ReturnNode extends Node {
|
||||
/** Gets the kind of this return node. */
|
||||
ReturnKind getKind() { any() }
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends CfgNode {
|
||||
OutNode() { node instanceof CallNode }
|
||||
/** A data flow node that represents a value returned by a callable. */
|
||||
class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
// See `TaintTrackingImplementation::returnFlowStep`
|
||||
ExtractedReturnNode() { node = any(Return ret).getValue().getAFlowNode() }
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
}
|
||||
|
||||
/** A data-flow node that represents the output of a call. */
|
||||
abstract class OutNode extends Node {
|
||||
/** Gets the underlying call, where this node is a corresponding output of kind `kind`. */
|
||||
abstract DataFlowCall getCall(ReturnKind kind);
|
||||
}
|
||||
|
||||
private module OutNodes {
|
||||
/**
|
||||
* A data-flow node that reads a value returned directly by a callable.
|
||||
*/
|
||||
class ExprOutNode extends OutNode, ExprNode {
|
||||
private DataFlowCall call;
|
||||
|
||||
ExprOutNode() { call.(ExtractedDataFlowCall).getNode() = this.getNode() }
|
||||
|
||||
override DataFlowCall getCall(ReturnKind kind) {
|
||||
result = call and
|
||||
kind = kind
|
||||
}
|
||||
}
|
||||
|
||||
private class SummaryOutNode extends SummaryNode, OutNode {
|
||||
SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this, _) }
|
||||
|
||||
override DataFlowCall getCall(ReturnKind kind) {
|
||||
FlowSummaryImpl::Private::summaryOutNode(result, this, kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that can read the value returned from `call` with return kind
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
call.getNode() = result.getNode() and
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { call = result.getCall(kind) }
|
||||
|
|
|
@ -2,6 +2,7 @@ private import python
|
|||
private import DataFlowPublic
|
||||
private import semmle.python.essa.SsaCompute
|
||||
private import semmle.python.dataflow.new.internal.ImportResolution
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
// Since we allow extra data-flow steps from modeled frameworks, we import these
|
||||
// up-front, to ensure these are included. This provides a more seamless experience from
|
||||
// a user point of view, since they don't need to know they need to import a specific
|
||||
|
@ -21,7 +22,7 @@ import DataFlowDispatchPointsTo
|
|||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
|
||||
predicate isParameterNode(ParameterNodeImpl p, DataFlowCallable c, ParameterPosition pos) {
|
||||
p.isParameterOf(c, pos)
|
||||
}
|
||||
|
||||
|
@ -77,7 +78,7 @@ module SyntheticPreUpdateNode {
|
|||
* that is mapped to the `self` parameter. That way, constructor calls represent the value of the
|
||||
* object after the constructor (currently only `__init__`) has run.
|
||||
*/
|
||||
CfgNode objectCreationNode() { result.getNode().(CallNode) = any(ClassCall c).getNode() }
|
||||
CfgNode objectCreationNode() { result.getNode() = any(ClassCall c).getNode() }
|
||||
}
|
||||
|
||||
import SyntheticPreUpdateNode
|
||||
|
@ -87,6 +88,8 @@ deprecated module syntheticPostUpdateNode = SyntheticPostUpdateNode;
|
|||
|
||||
/** A module collecting the different reasons for synthesising a post-update node. */
|
||||
module SyntheticPostUpdateNode {
|
||||
private import semmle.python.SpecialMethods
|
||||
|
||||
/** A post-update node is synthesized for all nodes which satisfy `NeedsSyntheticPostUpdateNode`. */
|
||||
class SyntheticPostUpdateNode extends PostUpdateNode, TSyntheticPostUpdateNode {
|
||||
NeedsSyntheticPostUpdateNode pre;
|
||||
|
@ -136,6 +139,8 @@ module SyntheticPostUpdateNode {
|
|||
Node argumentPreUpdateNode() {
|
||||
result = any(FunctionCall c).getArg(_)
|
||||
or
|
||||
result = any(LambdaCall c).getArg(_)
|
||||
or
|
||||
// Avoid argument 0 of method calls as those have read post-update nodes.
|
||||
exists(MethodCall c, int n | n > 0 | result = c.getArg(n))
|
||||
or
|
||||
|
@ -145,11 +150,18 @@ module SyntheticPostUpdateNode {
|
|||
exists(ClassCall c, int n | n > 0 | result = c.getArg(n))
|
||||
or
|
||||
// any argument of any call that we have not been able to resolve
|
||||
exists(CallNode call | not call = any(DataFlowCall c).getNode() |
|
||||
exists(CallNode call | not resolvedCall(call) |
|
||||
result.(CfgNode).getNode() in [call.getArg(_), call.getArgByName(_)]
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `call` can be resolved as a normal call */
|
||||
private predicate resolvedCall(CallNode call) {
|
||||
call = any(DataFlowCallableValue cv).getACall()
|
||||
or
|
||||
call = any(DataFlowLambda l).getACall()
|
||||
}
|
||||
|
||||
/** Gets the pre-update node associated with a store. This is used for when an object might have its value changed after a store. */
|
||||
CfgNode storePreUpdateNode() {
|
||||
exists(Attribute a |
|
||||
|
@ -287,10 +299,22 @@ module EssaFlow {
|
|||
* This is the local flow predicate that is used as a building block in global
|
||||
* data flow.
|
||||
*
|
||||
* It includes flow steps from flow summaries.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStepForTypetracking(nodeFrom, nodeTo)
|
||||
or
|
||||
summaryFlowSteps(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the local flow predicate that is used as a building block in
|
||||
* type tracking, it does _not_ include steps from flow summaries.
|
||||
*
|
||||
* Local flow can happen either at import time, when the module is initialised
|
||||
* or at runtime when callables in the module are called.
|
||||
*/
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
predicate simpleLocalFlowStepForTypetracking(Node nodeFrom, Node nodeTo) {
|
||||
// If there is local flow out of a node `node`, we want flow
|
||||
// both out of `node` and any post-update node of `node`.
|
||||
exists(Node node |
|
||||
|
@ -326,6 +350,34 @@ predicate runtimeLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
EssaFlow::essaFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
predicate summaryFlowSteps(Node nodeFrom, Node nodeTo) {
|
||||
// If there is local flow out of a node `node`, we want flow
|
||||
// both out of `node` and any post-update node of `node`.
|
||||
exists(Node node |
|
||||
nodeFrom = update(node) and
|
||||
(
|
||||
importTimeSummaryFlowStep(node, nodeTo) or
|
||||
runtimeSummaryFlowStep(node, nodeTo)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate importTimeSummaryFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// As a proxy for whether statements can be executed at import time,
|
||||
// we check if they appear at the top level.
|
||||
// This will miss statements inside functions called from the top level.
|
||||
isTopLevel(nodeFrom) and
|
||||
isTopLevel(nodeTo) and
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
|
||||
}
|
||||
|
||||
predicate runtimeSummaryFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Anything not at the top level can be executed at runtime.
|
||||
not isTopLevel(nodeFrom) and
|
||||
not isTopLevel(nodeTo) and
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
|
||||
}
|
||||
|
||||
/** `ModuleVariable`s are accessed via jump steps at runtime. */
|
||||
predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
|
||||
// Module variable read
|
||||
|
@ -474,6 +526,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
matchStoreStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
any(Orm::AdditionalOrmSteps es).storeStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -667,6 +721,8 @@ predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
attributeReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
kwUnpackReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
|
||||
/** Data flows from a sequence to a subscript of the sequence. */
|
||||
|
@ -791,6 +847,8 @@ predicate clearsContent(Node n, Content c) {
|
|||
matchClearStep(n, c)
|
||||
or
|
||||
attributeClearStep(n, c)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -842,15 +900,38 @@ int accessPathLimit() { result = 5 }
|
|||
predicate forceHighPrecision(Content c) { none() }
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { none() }
|
||||
predicate nodeIsHidden(Node n) {
|
||||
n instanceof SummaryNode
|
||||
or
|
||||
n instanceof SummaryParameterNode
|
||||
}
|
||||
|
||||
class LambdaCallKind = Unit;
|
||||
|
||||
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) {
|
||||
// lambda
|
||||
kind = kind and
|
||||
creation.asExpr() = c.(DataFlowLambda).getDefinition()
|
||||
or
|
||||
// normal function
|
||||
exists(FunctionDef def |
|
||||
def.defines(creation.asVar().getSourceVariable()) and
|
||||
def.getDefinedFunction() = c.(DataFlowCallableValue).getCallableValue().getScope()
|
||||
)
|
||||
or
|
||||
// summarized function
|
||||
exists(Call call |
|
||||
creation.asExpr() = call.getAnArg() and
|
||||
creation = c.(LibraryCallableValue).getACallback()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
|
||||
receiver = call.(SummaryCall).getReceiver() and
|
||||
exists(kind)
|
||||
}
|
||||
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
@ -862,4 +943,6 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
|||
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) {
|
||||
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import Attributes
|
|||
import LocalSources
|
||||
private import semmle.python.essa.SsaCompute
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
/**
|
||||
* IPA type for data flow nodes.
|
||||
|
@ -100,7 +101,17 @@ newtype TNode =
|
|||
//
|
||||
// So for now we live with having these synthetic ORM nodes for _all_ classes, which
|
||||
// is a bit wasteful, but we don't think it will hurt too much.
|
||||
TSyntheticOrmModelNode(Class cls)
|
||||
TSyntheticOrmModelNode(Class cls) or
|
||||
TSummaryNode(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryNodeRange(c, state)
|
||||
} or
|
||||
TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) {
|
||||
FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos)
|
||||
}
|
||||
|
||||
class TParameterNode = TCfgNode or TSummaryParameterNode;
|
||||
|
||||
/** Helper for `Node::getEnclosingCallable`. */
|
||||
private DataFlowCallable getCallableScope(Scope s) {
|
||||
|
@ -277,40 +288,56 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
|
|||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ParameterNode extends CfgNode, LocalSourceNode {
|
||||
class ParameterNode extends Node, TParameterNode instanceof ParameterNodeImpl {
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
final Parameter getParameter() { result = super.getParameter() }
|
||||
}
|
||||
|
||||
/** A parameter node found in the source code (not in a summary). */
|
||||
class ExtractedParameterNode extends ParameterNodeImpl, CfgNode {
|
||||
//, LocalSourceNode {
|
||||
ParameterDefinition def;
|
||||
|
||||
ParameterNode() {
|
||||
ExtractedParameterNode() {
|
||||
node = def.getDefiningNode() and
|
||||
// Disregard parameters that we cannot resolve
|
||||
// TODO: Make this unnecessary
|
||||
exists(DataFlowCallable c | node = c.getParameter(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of callable `c` at the
|
||||
* (zero-based) index `i`.
|
||||
*/
|
||||
predicate isParameterOf(DataFlowCallable c, int i) { node = c.getParameter(i) }
|
||||
override predicate isParameterOf(DataFlowCallable c, int i) { node = c.getParameter(i) }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { this.isParameterOf(result, _) }
|
||||
|
||||
/** Gets the `Parameter` this `ParameterNode` represents. */
|
||||
Parameter getParameter() { result = def.getParameter() }
|
||||
override Parameter getParameter() { result = def.getParameter() }
|
||||
}
|
||||
|
||||
class LocalSourceParameterNode extends ExtractedParameterNode, LocalSourceNode { }
|
||||
|
||||
/** Gets a node corresponding to parameter `p`. */
|
||||
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
ExtractedParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
|
||||
/** A data flow node that represents a call argument. */
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() { this = any(DataFlowCall c).getArg(_) }
|
||||
|
||||
abstract class ArgumentNode extends Node {
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
predicate argumentOf(DataFlowCall call, int pos) { this = call.getArg(pos) }
|
||||
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
final DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
/** Gets the call in which this node is an argument, if any. */
|
||||
final ExtractedDataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/** A data flow node that represents a call argument found in the source code. */
|
||||
class ExtractedArgumentNode extends ArgumentNode {
|
||||
ExtractedArgumentNode() { this = any(ExtractedDataFlowCall c).getArg(_) }
|
||||
|
||||
final override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
this.extractedArgumentOf(call, pos)
|
||||
}
|
||||
|
||||
predicate extractedArgumentOf(ExtractedDataFlowCall call, ArgumentPosition pos) {
|
||||
this = call.getArg(pos)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -390,7 +417,7 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
|
|||
|
||||
/** Gets an `EssaNode` that corresponds to an assignment of this global variable. */
|
||||
EssaNode getAWrite() {
|
||||
result.asVar().getDefinition().(EssaNodeDefinition).definedBy(var, any(DefinitionNode defn))
|
||||
result.getVar().getDefinition().(EssaNodeDefinition).definedBy(var, any(DefinitionNode defn))
|
||||
}
|
||||
|
||||
/** Gets the possible values of the variable at the end of import time */
|
||||
|
|
|
@ -5,12 +5,20 @@
|
|||
private import python
|
||||
private import DataFlowPrivate
|
||||
import DataFlowPublic
|
||||
private import FlowSummaryImpl as FlowSummaryImpl
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Simple flow through library code is included in the exposed local
|
||||
// step relation, even though flow is technically inter-procedural.
|
||||
// This is a convention followed across languages.
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,225 @@
|
|||
/**
|
||||
* Provides Python specific classes and predicates for defining flow summaries.
|
||||
*
|
||||
* Flow summaries are defined for callables that are not extracted.
|
||||
* Such callables go by different names in different parts of our codebase:
|
||||
*
|
||||
* - in `FlowSummary.qll`, which is user facing, they are called `SummarizedCallable`s.
|
||||
* These contain summaries, implemented by the user via the predicates `propagatesFlow` and `propagatesFlowExt`.
|
||||
*
|
||||
* - in the data flow layer, they are called `LibraryCallable`s (as in the Ruby codebase).
|
||||
* These are identified by strings and has predicates for finding calls to them.
|
||||
*
|
||||
* Having both extracted and non-extracted callables means that we now have three types of calls:
|
||||
* - Extracted calls to extracted callables, either `NormalCall` or `SpecialCall`. These are handled by standard data flow.
|
||||
* - Extracted calls to non-extracted callables, `LibraryCall`. These are handled by loking up the relevant summary when the
|
||||
* global data flwo graph is connected up via `getViableCallable`.
|
||||
* - Non-extracted calls, `SummaryCall`. These are synthesised by the flow summary framework.
|
||||
*
|
||||
* The first two can be referred to as `ExtractedDataFlowCall`. In fact, `LibraryCall` is a subclass of `NormalCall`, where
|
||||
* `getCallable` is set to `none()`. The member predicate `ExtractedDataFlowCall::getCallable` is _not_ the mechanism for
|
||||
* call resolution in global data flow. That mechanism is `getViableCallable`.
|
||||
* Resolving a call to a non-extracted callable goes via `LibraryCallable::getACall`, which may involve type tracking.
|
||||
* To avoid that type tracking becomes mutualy recursive with data flow, type tracking must use a call graph not including summaries.
|
||||
* Type tracking sees the callgraph given by `ExtractedDataFlowCall::getACallable`.
|
||||
*
|
||||
* We do not support summaries of special methods via the special methods framework,
|
||||
* the summary would have to identify the call.
|
||||
*
|
||||
* We might, while we still extract the standard library, want to support flow summaries of
|
||||
* extracted callables, so that we can model part of the standard library with flow summaries.
|
||||
* For this to work, we have be careful with the enclosing callable predicate.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowPublic
|
||||
private import DataFlowImplCommon
|
||||
private import FlowSummaryImpl::Private
|
||||
private import FlowSummaryImpl::Public
|
||||
private import semmle.python.dataflow.new.FlowSummary as FlowSummary
|
||||
|
||||
class SummarizedCallableBase = string;
|
||||
|
||||
/** View a `SummarizedCallable` as a `DataFlowCallable`. */
|
||||
DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c }
|
||||
|
||||
/** Gets the parameter position of the instance parameter. */
|
||||
ArgumentPosition instanceParameterPosition() { none() } // disables implicit summary flow to `this` for callbacks
|
||||
|
||||
/** Gets the synthesized summary data-flow node for the given values. */
|
||||
Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) }
|
||||
|
||||
/** Gets the synthesized data-flow call for `receiver`. */
|
||||
SummaryCall summaryDataFlowCall(Node receiver) { receiver = result.getReceiver() }
|
||||
|
||||
/** Gets the type of content `c`. */
|
||||
DataFlowType getContentType(Content c) { any() }
|
||||
|
||||
/** Gets the return type of kind `rk` for callable `c`. */
|
||||
bindingset[c, rk]
|
||||
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
|
||||
|
||||
/**
|
||||
* Gets the type of the `i`th parameter in a synthesized call that targets a
|
||||
* callback of type `t`.
|
||||
*/
|
||||
bindingset[t, i]
|
||||
DataFlowType getCallbackParameterType(DataFlowType t, int i) { any() }
|
||||
|
||||
/**
|
||||
* Gets the return type of kind `rk` in a synthesized call that targets a
|
||||
* callback of type `t`.
|
||||
*/
|
||||
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
|
||||
|
||||
/**
|
||||
* Holds if an external flow summary exists for `c` with input specification
|
||||
* `input`, output specification `output`, kind `kind`, and a flag `generated`
|
||||
* stating whether the summary is autogenerated.
|
||||
*/
|
||||
predicate summaryElement(
|
||||
FlowSummary::SummarizedCallable c, string input, string output, string kind, boolean generated
|
||||
) {
|
||||
exists(boolean preservesValue |
|
||||
c.propagatesFlowExt(input, output, preservesValue) and
|
||||
(if preservesValue = true then kind = "value" else kind = "taint") and
|
||||
generated = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a negative flow summary exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the summary is autogenerated.
|
||||
* Note. Negative flow summaries has not been implemented for Python.
|
||||
*/
|
||||
predicate negativeSummaryElement(FlowSummary::SummarizedCallable c, boolean generated) { none() }
|
||||
|
||||
/**
|
||||
* Gets the summary component for specification component `c`, if any.
|
||||
*
|
||||
* This covers all the Python-specific components of a flow summary.
|
||||
*/
|
||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
||||
c = "ListElement" and
|
||||
result = FlowSummary::SummaryComponent::listElement()
|
||||
}
|
||||
|
||||
/** Gets the textual representation of a summary component in the format used for flow summaries. */
|
||||
string getComponentSpecificCsv(SummaryComponent sc) {
|
||||
sc = TContentSummaryComponent(any(ListElementContent c)) and
|
||||
result = "ListElement"
|
||||
}
|
||||
|
||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
||||
string getParameterPositionCsv(ParameterPosition pos) { result = pos.toString() }
|
||||
|
||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
||||
string getArgumentPositionCsv(ArgumentPosition pos) { result = pos.toString() }
|
||||
|
||||
/** Holds if input specification component `c` needs a reference. */
|
||||
predicate inputNeedsReferenceSpecific(string c) { none() }
|
||||
|
||||
/** Holds if output specification component `c` needs a reference. */
|
||||
predicate outputNeedsReferenceSpecific(string c) { none() }
|
||||
|
||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
||||
ReturnKind getReturnValueKind() { any() }
|
||||
|
||||
/**
|
||||
* All definitions in this module are required by the shared implementation
|
||||
* (for source/sink interpretation), but they are unused for Python, where
|
||||
* we rely on API graphs instead.
|
||||
*/
|
||||
private module UnusedSourceSinkInterpretation {
|
||||
/**
|
||||
* Holds if an external source specification exists for `n` with output specification
|
||||
* `output`, kind `kind`, and a flag `generated` stating whether the source specification is
|
||||
* autogenerated.
|
||||
*/
|
||||
predicate sourceElement(AstNode n, string output, string kind, boolean generated) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `n` with input specification
|
||||
* `input`, kind `kind` and a flag `generated` stating whether the sink specification is
|
||||
* autogenerated.
|
||||
*/
|
||||
predicate sinkElement(AstNode n, string input, string kind, boolean generated) { none() }
|
||||
|
||||
class SourceOrSinkElement = AstNode;
|
||||
|
||||
/** An entity used to interpret a source/sink specification. */
|
||||
class InterpretNode extends AstNode_ {
|
||||
// InterpretNode is going away, this is just a dummy implementation.
|
||||
// However, we have some old location tests picking them up, so we
|
||||
// explicitly define them to not exist.
|
||||
InterpretNode() { none() }
|
||||
|
||||
/** Gets the element that this node corresponds to, if any. */
|
||||
SourceOrSinkElement asElement() { none() }
|
||||
|
||||
/** Gets the data-flow node that this node corresponds to, if any. */
|
||||
Node asNode() { none() }
|
||||
|
||||
/** Gets the call that this node corresponds to, if any. */
|
||||
DataFlowCall asCall() { none() }
|
||||
|
||||
/** Gets the callable that this node corresponds to, if any. */
|
||||
DataFlowCallable asCallable() { none() }
|
||||
|
||||
/** Gets the target of this call, if any. */
|
||||
SourceOrSinkElement getCallTarget() { none() }
|
||||
}
|
||||
|
||||
/** Provides additional sink specification logic. */
|
||||
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
|
||||
|
||||
/** Provides additional source specification logic. */
|
||||
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
|
||||
}
|
||||
|
||||
import UnusedSourceSinkInterpretation
|
||||
|
||||
module ParsePositions {
|
||||
private import FlowSummaryImpl
|
||||
|
||||
private predicate isParamBody(string body) {
|
||||
exists(AccessPathToken tok |
|
||||
tok.getName() = "Parameter" and
|
||||
body = tok.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isArgBody(string body) {
|
||||
exists(AccessPathToken tok |
|
||||
tok.getName() = "Argument" and
|
||||
body = tok.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isParsedParameterPosition(string c, int i) {
|
||||
isParamBody(c) and
|
||||
i = AccessPath::parseInt(c)
|
||||
}
|
||||
|
||||
predicate isParsedArgumentPosition(string c, int i) {
|
||||
isArgBody(c) and
|
||||
i = AccessPath::parseInt(c)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
||||
ArgumentPosition parseParamBody(string s) {
|
||||
exists(int i |
|
||||
ParsePositions::isParsedParameterPosition(s, i) and
|
||||
result.isPositional(i)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
|
||||
ParameterPosition parseArgBody(string s) {
|
||||
exists(int i |
|
||||
ParsePositions::isParsedArgumentPosition(s, i) and
|
||||
result.isPositional(i)
|
||||
)
|
||||
}
|
|
@ -244,7 +244,7 @@ class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instan
|
|||
*/
|
||||
predicate iterableUnpackingAssignmentFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(AssignmentTarget target |
|
||||
nodeFrom.asExpr() = target.getValue() and
|
||||
nodeFrom.(CfgNode).getNode().getNode() = target.getValue() and
|
||||
nodeTo = TIterableSequenceNode(target)
|
||||
)
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ predicate iterableUnpackingAssignmentFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
*/
|
||||
predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
|
||||
exists(ForTarget target |
|
||||
nodeFrom.asExpr() = target.getSource() and
|
||||
nodeFrom.getNode().getNode() = target.getSource() and
|
||||
target instanceof SequenceNode and
|
||||
nodeTo = TIterableSequenceNode(target)
|
||||
) and
|
||||
|
@ -273,7 +273,7 @@ predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo)
|
|||
predicate iterableUnpackingTupleFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(UnpackingAssignmentSequenceTarget target |
|
||||
nodeFrom = TIterableSequenceNode(target) and
|
||||
nodeTo.asCfgNode() = target
|
||||
nodeTo.(CfgNode).getNode() = target
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -305,7 +305,7 @@ predicate iterableUnpackingConvertingReadStep(Node nodeFrom, Content c, Node nod
|
|||
predicate iterableUnpackingConvertingStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
exists(UnpackingAssignmentSequenceTarget target |
|
||||
nodeFrom = TIterableElementNode(target) and
|
||||
nodeTo.asCfgNode() = target and
|
||||
nodeTo.(CfgNode).getNode() = target and
|
||||
exists(int index | exists(target.getElement(index)) |
|
||||
c.(TupleElementContent).getIndex() = index
|
||||
)
|
||||
|
@ -331,7 +331,7 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
|
|||
not exists(target.getAnElement().(StarredNode)) and
|
||||
starIndex = -1
|
||||
|
|
||||
nodeFrom.asCfgNode() = target and
|
||||
nodeFrom.(CfgNode).getNode() = target and
|
||||
element = target.getElement(index) and
|
||||
(
|
||||
if starIndex = -1 or index < starIndex
|
||||
|
|
|
@ -36,7 +36,7 @@ class LocalSourceNode extends Node {
|
|||
LocalSourceNode() {
|
||||
Stages::DataFlow::ref() and
|
||||
this instanceof ExprNode and
|
||||
not simpleLocalFlowStep(_, this)
|
||||
not simpleLocalFlowStepForTypetracking(_, this)
|
||||
or
|
||||
// We include all module variable nodes, as these act as stepping stones between writes and
|
||||
// reads of global variables. Without them, type tracking based on `LocalSourceNode`s would be
|
||||
|
|
|
@ -69,8 +69,8 @@ predicate matchSubjectFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
subject = match.getSubject() and
|
||||
target = match.getCase(_).(Case).getPattern()
|
||||
|
|
||||
nodeFrom.asExpr() = subject and
|
||||
nodeTo.asCfgNode().getNode() = target
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo.(CfgNode).getNode().getNode() = target
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -84,12 +84,13 @@ predicate matchAsFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
// That way, information can propagate from the interior pattern to the alias.
|
||||
//
|
||||
// the subject flows to the interior pattern
|
||||
nodeFrom.asCfgNode().getNode() = subject and
|
||||
nodeTo.asCfgNode().getNode() = subject.getPattern()
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo.(CfgNode).getNode().getNode() = subject.getPattern()
|
||||
or
|
||||
// the interior pattern flows to the alias
|
||||
nodeFrom.asCfgNode().getNode() = subject.getPattern() and
|
||||
nodeTo.asVar().getDefinition().(PatternAliasDefinition).getDefiningNode().getNode() = alias
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject.getPattern() and
|
||||
nodeTo.(EssaNode).getVar().getDefinition().(PatternAliasDefinition).getDefiningNode().getNode() =
|
||||
alias
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -99,8 +100,8 @@ predicate matchAsFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
*/
|
||||
predicate matchOrFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(MatchOrPattern subject, Pattern pattern | pattern = subject.getAPattern() |
|
||||
nodeFrom.asCfgNode().getNode() = subject and
|
||||
nodeTo.asCfgNode().getNode() = pattern
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo.(CfgNode).getNode().getNode() = pattern
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -110,8 +111,8 @@ predicate matchOrFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
*/
|
||||
predicate matchLiteralFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(MatchLiteralPattern pattern, Expr literal | literal = pattern.getLiteral() |
|
||||
nodeFrom.asExpr() = literal and
|
||||
nodeTo.asCfgNode().getNode() = pattern
|
||||
nodeFrom.(CfgNode).getNode().getNode() = literal and
|
||||
nodeTo.(CfgNode).getNode().getNode() = pattern
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -121,8 +122,14 @@ predicate matchLiteralFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
*/
|
||||
predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(MatchCapturePattern capture, Name var | capture.getVariable() = var |
|
||||
nodeFrom.asCfgNode().getNode() = capture and
|
||||
nodeTo.asVar().getDefinition().(PatternCaptureDefinition).getDefiningNode().getNode() = var
|
||||
nodeFrom.(CfgNode).getNode().getNode() = capture and
|
||||
nodeTo
|
||||
.(EssaNode)
|
||||
.getVar()
|
||||
.getDefinition()
|
||||
.(PatternCaptureDefinition)
|
||||
.getDefiningNode()
|
||||
.getNode() = var
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -132,8 +139,8 @@ predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) {
|
|||
*/
|
||||
predicate matchValueFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(MatchValuePattern pattern, Expr value | value = pattern.getValue() |
|
||||
nodeFrom.asExpr() = value and
|
||||
nodeTo.asCfgNode().getNode() = pattern
|
||||
nodeFrom.(CfgNode).getNode().getNode() = value and
|
||||
nodeTo.(CfgNode).getNode().getNode() = pattern
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -145,8 +152,8 @@ predicate matchSequenceReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
exists(MatchSequencePattern subject, int index, Pattern element |
|
||||
element = subject.getPattern(index)
|
||||
|
|
||||
nodeFrom.asCfgNode().getNode() = subject and
|
||||
nodeTo.asCfgNode().getNode() = element and
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo.(CfgNode).getNode().getNode() = element and
|
||||
(
|
||||
// tuple content
|
||||
c.(TupleElementContent).getIndex() = index
|
||||
|
@ -173,7 +180,7 @@ predicate matchStarReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
exists(MatchSequencePattern subject, int index, MatchStarPattern star |
|
||||
star = subject.getPattern(index)
|
||||
|
|
||||
nodeFrom.asCfgNode().getNode() = subject and
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo = TStarPatternElementNode(star) and
|
||||
(
|
||||
// tuple content
|
||||
|
@ -200,7 +207,7 @@ predicate matchStarReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
predicate matchStarStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
exists(MatchStarPattern star |
|
||||
nodeFrom = TStarPatternElementNode(star) and
|
||||
nodeTo.asCfgNode().getNode() = star.getTarget() and
|
||||
nodeTo.(CfgNode).getNode().getNode() = star.getTarget() and
|
||||
c instanceof ListElementContent
|
||||
)
|
||||
}
|
||||
|
@ -218,8 +225,8 @@ predicate matchMappingReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
key = keyValue.getKey() and
|
||||
value = keyValue.getValue()
|
||||
|
|
||||
nodeFrom.asCfgNode().getNode() = subject and
|
||||
nodeTo.asCfgNode().getNode() = value and
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo.(CfgNode).getNode().getNode() = value and
|
||||
c.(DictionaryElementContent).getKey() = key.getLiteral().(StrConst).getText()
|
||||
)
|
||||
}
|
||||
|
@ -233,8 +240,8 @@ predicate matchMappingReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
*/
|
||||
predicate matchMappingFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
exists(MatchMappingPattern subject, MatchDoubleStarPattern dstar | dstar = subject.getAMapping() |
|
||||
nodeFrom.asCfgNode().getNode() = subject and
|
||||
nodeTo.asCfgNode().getNode() = dstar.getTarget()
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo.(CfgNode).getNode().getNode() = dstar.getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -251,7 +258,7 @@ predicate matchMappingClearStep(Node n, Content c) {
|
|||
key = keyValue.getKey() and
|
||||
dstar = subject.getAMapping()
|
||||
|
|
||||
n.asCfgNode().getNode() = dstar.getTarget() and
|
||||
n.(CfgNode).getNode().getNode() = dstar.getTarget() and
|
||||
c.(DictionaryElementContent).getKey() = key.getLiteral().(StrConst).getText()
|
||||
)
|
||||
}
|
||||
|
@ -266,8 +273,8 @@ predicate matchClassReadStep(Node nodeFrom, Content c, Node nodeTo) {
|
|||
attr = keyword.getAttribute() and
|
||||
value = keyword.getValue()
|
||||
|
|
||||
nodeFrom.asCfgNode().getNode() = subject and
|
||||
nodeTo.asCfgNode().getNode() = value and
|
||||
nodeFrom.(CfgNode).getNode().getNode() = subject and
|
||||
nodeTo.(CfgNode).getNode().getNode() = value and
|
||||
c.(AttributeContent).getAttribute() = attr.getId()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class Node = DataFlowPublic::Node;
|
|||
|
||||
class TypeTrackingNode = DataFlowPublic::TypeTrackingNode;
|
||||
|
||||
predicate simpleLocalFlowStep = DataFlowPrivate::simpleLocalFlowStep/2;
|
||||
predicate simpleLocalFlowStep = DataFlowPrivate::simpleLocalFlowStepForTypetracking/2;
|
||||
|
||||
predicate jumpStep = DataFlowPrivate::jumpStepSharedWithTypeTracker/2;
|
||||
|
||||
|
@ -35,16 +35,22 @@ string getPossibleContentName() {
|
|||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowPrivate::DataFlowCallable getCallableForArgument(
|
||||
DataFlowPublic::ArgumentNode nodeFrom, int i
|
||||
DataFlowPublic::ExtractedArgumentNode nodeFrom, int i
|
||||
) {
|
||||
exists(DataFlowPrivate::DataFlowCall call |
|
||||
nodeFrom.argumentOf(call, i) and
|
||||
exists(DataFlowPrivate::ExtractedDataFlowCall call |
|
||||
nodeFrom.extractedArgumentOf(call, i) and
|
||||
result = call.getCallable()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call. */
|
||||
predicate callStep(DataFlowPublic::ArgumentNode nodeFrom, DataFlowPublic::ParameterNode nodeTo) {
|
||||
/**
|
||||
* Holds if `nodeFrom` steps to `nodeTo` by being passed as a parameter in a call.
|
||||
*
|
||||
* Flow into summarized library methods is not included, as that will lead to negative
|
||||
* recursion (or, at best, terrible performance), since identifying calls to library
|
||||
* methods is done using API graphs (which uses type tracking).
|
||||
*/
|
||||
predicate callStep(DataFlowPublic::ArgumentNode nodeFrom, DataFlowPrivate::ParameterNodeImpl nodeTo) {
|
||||
// TODO: Support special methods?
|
||||
exists(DataFlowPrivate::DataFlowCallable callable, int i |
|
||||
callable = getCallableForArgument(nodeFrom, i) and
|
||||
|
@ -54,8 +60,9 @@ predicate callStep(DataFlowPublic::ArgumentNode nodeFrom, DataFlowPublic::Parame
|
|||
|
||||
/** Holds if `nodeFrom` steps to `nodeTo` by being returned from a call. */
|
||||
predicate returnStep(DataFlowPrivate::ReturnNode nodeFrom, Node nodeTo) {
|
||||
exists(DataFlowPrivate::DataFlowCall call |
|
||||
nodeFrom.getEnclosingCallable() = call.getCallable() and nodeTo.asCfgNode() = call.getNode()
|
||||
exists(DataFlowPrivate::ExtractedDataFlowCall call |
|
||||
nodeFrom.getEnclosingCallable() = call.getCallable() and
|
||||
nodeTo.(DataFlowPublic::CfgNode).getNode() = call.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.TaintTracking
|
|||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.FlowSummary
|
||||
private import semmle.python.frameworks.PEP249
|
||||
private import semmle.python.frameworks.internal.PoorMansFunctionResolution
|
||||
private import semmle.python.frameworks.internal.SelfRefMixin
|
||||
|
@ -3670,6 +3671,23 @@ private module StdlibPrivate {
|
|||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getAnInput() }
|
||||
}
|
||||
|
||||
/** A flow summary for `reversed`. */
|
||||
class ReversedSummary extends SummarizedCallable {
|
||||
ReversedSummary() { this = "builtins.reversed" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() { result = API::builtin("reversed").getACall() }
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result = API::builtin("reversed").getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0].ListElement" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -26,14 +26,14 @@ import semmle.python.ApiGraphs::API as API
|
|||
class Unit = PY::Unit;
|
||||
|
||||
// Re-export libraries needed by ApiGraphModels.qll
|
||||
import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import semmle.python.dataflow.new.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import semmle.python.dataflow.new.DataFlow::DataFlow as DataFlow
|
||||
private import AccessPathSyntax
|
||||
|
||||
/**
|
||||
* Holds if models describing `package` may be relevant for the analysis of this database.
|
||||
*/
|
||||
predicate isPackageUsed(string package) { exists(API::moduleImport(package)) }
|
||||
predicate isPackageUsed(string package) { API::moduleImportExists(package) }
|
||||
|
||||
/** Gets a Python-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
|
||||
bindingset[package, type, path]
|
||||
|
|
|
@ -65,13 +65,17 @@ private class DefaultSafeExternalApi extends SafeExternalApi {
|
|||
|
||||
/** A node representing data being passed to an external API through a call. */
|
||||
class ExternalApiDataNode extends DataFlow::Node {
|
||||
DataFlowPrivate::DataFlowCall call;
|
||||
DataFlowPrivate::DataFlowCallable callable;
|
||||
int i;
|
||||
|
||||
ExternalApiDataNode() {
|
||||
exists(call.getLocation().getFile().getRelativePath()) and
|
||||
callable = call.getCallable() and
|
||||
exists(DataFlowPrivate::DataFlowCall call |
|
||||
exists(call.getLocation().getFile().getRelativePath())
|
||||
|
|
||||
callable = call.getCallable() and
|
||||
// TODO: this ignores some complexity of keyword arguments (especially keyword-only args)
|
||||
this = call.getArg(i)
|
||||
) and
|
||||
not any(SafeExternalApi safe).getSafeCallable() = callable and
|
||||
exists(Value cv | cv = callable.getCallableValue() |
|
||||
cv.isAbsent()
|
||||
|
@ -82,8 +86,6 @@ class ExternalApiDataNode extends DataFlow::Node {
|
|||
or
|
||||
not exists(cv.(CallableValue).getScope().getLocation().getFile().getRelativePath())
|
||||
) and
|
||||
// TODO: this ignores some complexity of keyword arguments (especially keyword-only args)
|
||||
this = call.getArg(i) and
|
||||
// Not already modeled as a taint step
|
||||
not exists(DataFlow::Node next | TaintTrackingPrivate::defaultAdditionalTaintStep(this, next)) and
|
||||
// for `list.append(x)`, we have a additional taint step from x -> [post] list.
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import python
|
||||
import experimental.dataflow.TestUtil.FlowTest
|
||||
import experimental.dataflow.testTaintConfig
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
class DataFlowTest extends FlowTest {
|
||||
DataFlowTest() { this = "DataFlowTest" }
|
||||
|
||||
override string flowTag() { result = "flow" }
|
||||
|
||||
override predicate relevantFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
exists(TestConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
}
|
||||
}
|
||||
|
||||
query predicate missingAnnotationOnSink(Location location, string error, string element) {
|
||||
error = "ERROR, you should add `# $ MISSING: flow` annotation" and
|
||||
exists(DataFlow::Node sink |
|
||||
exists(DataFlow::CallCfgNode call |
|
||||
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
|
||||
call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
|
||||
(sink = call.getArg(_) or sink = call.getArgByName(_))
|
||||
) and
|
||||
location = sink.getLocation() and
|
||||
element = prettyExpr(sink.asExpr()) and
|
||||
not any(TestConfiguration config).hasFlow(_, sink) and
|
||||
not exists(FalseNegativeExpectation missingResult |
|
||||
missingResult.getTag() = "flow" and
|
||||
missingResult.getLocation().getFile() = location.getFile() and
|
||||
missingResult.getLocation().getStartLine() = location.getStartLine()
|
||||
)
|
||||
)
|
||||
}
|
|
@ -2,6 +2,7 @@ import python
|
|||
import semmle.python.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
|
||||
|
||||
/**
|
||||
* A routing test is designed to test that values are routed to the
|
||||
|
|
|
@ -12,7 +12,13 @@ class UnresolvedCallExpectations extends InlineExpectationsTest {
|
|||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(CallNode call |
|
||||
not exists(DataFlowPrivate::DataFlowCall dfc | dfc.getNode() = call) and
|
||||
not exists(DataFlowPrivate::DataFlowCall dfc | dfc.getNode() = call |
|
||||
// For every `CallNode`, there is a `DataFlowCall` in the form of a `NormalCall`.
|
||||
// It does not really count, as it has some abstract overrides. For instance, it does not
|
||||
// define `getCallable`, so checking for the existence of this guarantees that we are in a
|
||||
// properly resolved call.
|
||||
exists(dfc.getCallable())
|
||||
) and
|
||||
not call = API::builtin(_).getACall().asCfgNode() and
|
||||
location = call.getLocation() and
|
||||
tag = "unresolved_call" and
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
| file://:0:0:0:0 | parameter 0 of builtins.reversed |
|
||||
| test.py:1:19:1:19 | ControlFlowNode for x |
|
||||
| test.py:7:5:7:20 | ControlFlowNode for obfuscated_id() |
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
|
||||
| test.py:4:10:4:10 | ControlFlowNode for z |
|
||||
| test.py:7:19:7:19 | ControlFlowNode for a |
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
| file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
|
||||
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
|
||||
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
|
||||
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
| file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
|
||||
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
|
||||
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
|
||||
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
| file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed |
|
||||
| file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
|
||||
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
|
||||
| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
|
||||
| file://:0:0:0:0 | parameter 0 of builtins.reversed | file://:0:0:0:0 | parameter 0 of builtins.reversed |
|
||||
| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:0:0:0:0 | GSSA Variable __name__ |
|
||||
| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:0:0:0:0 | GSSA Variable __package__ |
|
||||
| test.py:0:0:0:0 | GSSA Variable b | test.py:0:0:0:0 | GSSA Variable b |
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
| file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed | file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
|
||||
| test.py:1:1:1:21 | ControlFlowNode for FunctionExpr | test.py:1:5:1:17 | GSSA Variable obfuscated_id |
|
||||
| test.py:1:5:1:17 | GSSA Variable obfuscated_id | test.py:7:5:7:17 | ControlFlowNode for obfuscated_id |
|
||||
| test.py:1:19:1:19 | ControlFlowNode for x | test.py:1:19:1:19 | SSA variable x |
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
| file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed |
|
||||
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
|
||||
| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
|
||||
| file://:0:0:0:0 | parameter 0 of builtins.reversed |
|
||||
| test.py:0:0:0:0 | GSSA Variable __name__ |
|
||||
| test.py:0:0:0:0 | GSSA Variable __package__ |
|
||||
| test.py:0:0:0:0 | GSSA Variable b |
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
| file://:0:0:0:0 | [summary] read: argument 0.List element in builtins.reversed |
|
||||
| file://:0:0:0:0 | [summary] to write: return (return) in builtins.reversed |
|
||||
| file://:0:0:0:0 | [summary] to write: return (return).List element in builtins.reversed |
|
||||
| file://:0:0:0:0 | parameter 0 of builtins.reversed |
|
||||
| test.py:0:0:0:0 | GSSA Variable __name__ |
|
||||
| test.py:0:0:0:0 | GSSA Variable __package__ |
|
||||
| test.py:0:0:0:0 | GSSA Variable b |
|
||||
|
|
|
@ -31,7 +31,7 @@ try:
|
|||
# `mypkg.foo` is a `missing module variable`, but `mypkg.subpkg.bar` is compeltely
|
||||
# ignored.
|
||||
import mypkg
|
||||
mypkg.foo(42)
|
||||
mypkg.subpkg.bar(43)
|
||||
mypkg.foo(42) # $ call=mypkg.foo(..) qlclass=NormalCall
|
||||
mypkg.subpkg.bar(43) # $ call=mypkg.subpkg.bar(..) qlclass=LibraryCall arg_0=43
|
||||
except:
|
||||
pass
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
| classes.py:14:17:14:60 | ControlFlowNode for Attribute() | classes.py:14:17:14:60 | ControlFlowNode for Attribute() |
|
||||
| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() |
|
||||
| classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | ControlFlowNode for self |
|
||||
| classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() |
|
||||
| classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() |
|
||||
| classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() |
|
||||
| classes.py:559:16:559:17 | ControlFlowNode for Str | classes.py:565:5:565:22 | ControlFlowNode for Subscript |
|
||||
| classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | ControlFlowNode for self |
|
||||
| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | ControlFlowNode for key |
|
||||
| classes.py:581:5:581:16 | ControlFlowNode for with_setitem | classes.py:570:21:570:24 | ControlFlowNode for self |
|
||||
|
@ -11,29 +13,68 @@
|
|||
| classes.py:595:9:595:20 | ControlFlowNode for with_delitem | classes.py:586:21:586:24 | ControlFlowNode for self |
|
||||
| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:586:27:586:29 | ControlFlowNode for key |
|
||||
| classes.py:618:16:618:28 | ControlFlowNode for Attribute() | classes.py:618:16:618:28 | ControlFlowNode for Attribute() |
|
||||
| classes.py:659:15:659:18 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:661:16:661:19 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:657:17:657:20 | ControlFlowNode for self |
|
||||
| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:657:23:657:27 | ControlFlowNode for other |
|
||||
| classes.py:674:15:674:18 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:676:16:676:19 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:672:17:672:20 | ControlFlowNode for self |
|
||||
| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:672:23:672:27 | ControlFlowNode for other |
|
||||
| classes.py:689:15:689:18 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:691:16:691:19 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:687:17:687:20 | ControlFlowNode for self |
|
||||
| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:687:23:687:27 | ControlFlowNode for other |
|
||||
| classes.py:704:15:704:18 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:706:16:706:19 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:702:20:702:23 | ControlFlowNode for self |
|
||||
| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:702:26:702:30 | ControlFlowNode for other |
|
||||
| classes.py:719:15:719:18 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:721:16:721:19 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:717:21:717:24 | ControlFlowNode for self |
|
||||
| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:717:27:717:31 | ControlFlowNode for other |
|
||||
| classes.py:734:15:734:18 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:736:16:736:19 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:732:22:732:25 | ControlFlowNode for self |
|
||||
| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:732:28:732:32 | ControlFlowNode for other |
|
||||
| classes.py:749:15:749:18 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:751:16:751:19 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:747:17:747:20 | ControlFlowNode for self |
|
||||
| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:747:23:747:27 | ControlFlowNode for other |
|
||||
| classes.py:779:15:779:18 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:781:16:781:19 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:777:17:777:20 | ControlFlowNode for self |
|
||||
| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:777:23:777:27 | ControlFlowNode for other |
|
||||
| classes.py:800:15:800:18 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:802:16:802:19 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:798:20:798:23 | ControlFlowNode for self |
|
||||
| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:798:26:798:30 | ControlFlowNode for other |
|
||||
| classes.py:815:15:815:18 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:817:16:817:19 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:813:20:813:23 | ControlFlowNode for self |
|
||||
| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:813:26:813:30 | ControlFlowNode for other |
|
||||
| classes.py:830:15:830:18 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:832:16:832:19 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:828:17:828:20 | ControlFlowNode for self |
|
||||
| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:828:23:828:27 | ControlFlowNode for other |
|
||||
| classes.py:845:15:845:18 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:847:16:847:19 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:843:17:843:20 | ControlFlowNode for self |
|
||||
| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:843:23:843:27 | ControlFlowNode for other |
|
||||
| classes.py:860:15:860:18 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:862:16:862:19 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:858:16:858:19 | ControlFlowNode for self |
|
||||
| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr |
|
||||
| classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:858:22:858:26 | ControlFlowNode for other |
|
||||
|
|
|
@ -20,7 +20,7 @@ class CallGraphConfig extends DataFlow::Configuration {
|
|||
node instanceof DataFlow::ParameterNode and
|
||||
// exclude parameters to the SINK-functions
|
||||
not exists(DataFlowPrivate::DataFlowCallable c |
|
||||
node.(DataFlow::ParameterNode).isParameterOf(c, _) and
|
||||
c.getParameter(_) = node.asCfgNode() and
|
||||
c.getName().matches("SINK_")
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
missingAnnotationOnSink
|
||||
failures
|
|
@ -0,0 +1,3 @@
|
|||
import python
|
||||
private import TestSummaries
|
||||
import experimental.dataflow.TestUtil.NormalTaintTrackingTest
|
|
@ -0,0 +1,134 @@
|
|||
private import python
|
||||
private import semmle.python.dataflow.new.FlowSummary
|
||||
private import semmle.python.ApiGraphs
|
||||
|
||||
/**
|
||||
* This module ensures that the `callStep` predicate in
|
||||
* our type tracker implelemtation does not refer to the
|
||||
* `getACall` predicate on `SummarizedCallable`.
|
||||
*/
|
||||
module RecursionGuard {
|
||||
private import semmle.python.dataflow.new.internal.TypeTrackerSpecific as TT
|
||||
|
||||
private class RecursionGuard extends SummarizedCallable {
|
||||
RecursionGuard() { this = "RecursionGuard" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.getFunction().asCfgNode().(NameNode).getId() = this and
|
||||
(TT::callStep(_, _) implies any())
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableIdentity extends SummarizedCallable {
|
||||
SummarizedCallableIdentity() { this = "identity" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.getFunction().asCfgNode().(NameNode).getId() = this
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
// For lambda flow to work, implement lambdaCall and lambdaCreation
|
||||
private class SummarizedCallableApplyLambda extends SummarizedCallable {
|
||||
SummarizedCallableApplyLambda() { this = "apply_lambda" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.getFunction().asCfgNode().(NameNode).getId() = this
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[1]" and
|
||||
output = "Argument[0].Parameter[0]" and
|
||||
preservesValue = true
|
||||
or
|
||||
input = "Argument[0].ReturnValue" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableReversed extends SummarizedCallable {
|
||||
SummarizedCallableReversed() { this = "reversed" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.getFunction().asCfgNode().(NameNode).getId() = this
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0].ListElement" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableMap extends SummarizedCallable {
|
||||
SummarizedCallableMap() { this = "list_map" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.getFunction().asCfgNode().(NameNode).getId() = this
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[1].ListElement" and
|
||||
output = "Argument[0].Parameter[0]" and
|
||||
preservesValue = true
|
||||
or
|
||||
input = "Argument[0].ReturnValue" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableAppend extends SummarizedCallable {
|
||||
SummarizedCallableAppend() { this = "append_to_list" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result.getFunction().asCfgNode().(NameNode).getId() = this
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = false
|
||||
or
|
||||
input = "Argument[1]" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
private class SummarizedCallableJsonLoads extends SummarizedCallable {
|
||||
SummarizedCallableJsonLoads() { this = "json.loads" }
|
||||
|
||||
override DataFlow::CallCfgNode getACall() {
|
||||
result = API::moduleImport("json").getMember("loads").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::ArgumentNode getACallback() {
|
||||
result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0]" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
edges
|
||||
| summaries.py:32:11:32:26 | ControlFlowNode for identity() | summaries.py:33:6:33:12 | ControlFlowNode for tainted |
|
||||
| summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:32:11:32:26 | ControlFlowNode for identity() |
|
||||
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda |
|
||||
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() |
|
||||
| summaries.py:44:25:44:32 | ControlFlowNode for List | summaries.py:45:6:45:20 | ControlFlowNode for Subscript |
|
||||
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:44:25:44:32 | ControlFlowNode for List |
|
||||
| summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] |
|
||||
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | summaries.py:51:38:51:45 | ControlFlowNode for List [List element] |
|
||||
| summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] | summaries.py:52:6:52:22 | ControlFlowNode for Subscript |
|
||||
| summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] |
|
||||
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | summaries.py:57:55:57:62 | ControlFlowNode for List [List element] |
|
||||
| summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] | summaries.py:58:6:58:31 | ControlFlowNode for Subscript |
|
||||
| summaries.py:60:26:60:53 | ControlFlowNode for list_map() [List element] | summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] |
|
||||
| summaries.py:60:45:60:52 | ControlFlowNode for List [List element] | summaries.py:60:26:60:53 | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | summaries.py:60:45:60:52 | ControlFlowNode for List [List element] |
|
||||
| summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] | summaries.py:61:6:61:30 | ControlFlowNode for Subscript |
|
||||
| summaries.py:63:16:63:41 | ControlFlowNode for append_to_list() [List element] | summaries.py:64:6:64:17 | ControlFlowNode for tainted_list [List element] |
|
||||
| summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | summaries.py:63:16:63:41 | ControlFlowNode for append_to_list() [List element] |
|
||||
| summaries.py:64:6:64:17 | ControlFlowNode for tainted_list [List element] | summaries.py:64:6:64:20 | ControlFlowNode for Subscript |
|
||||
| summaries.py:67:22:67:39 | ControlFlowNode for json_loads() [List element] | summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] |
|
||||
| summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | summaries.py:67:22:67:39 | ControlFlowNode for json_loads() [List element] |
|
||||
| summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | summaries.py:68:6:68:26 | ControlFlowNode for Subscript |
|
||||
| summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] | summaries.py:68:6:68:26 | ControlFlowNode for Subscript |
|
||||
nodes
|
||||
| summaries.py:32:11:32:26 | ControlFlowNode for identity() | semmle.label | ControlFlowNode for identity() |
|
||||
| summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | semmle.label | ControlFlowNode for tainted |
|
||||
| summaries.py:36:18:36:54 | ControlFlowNode for apply_lambda() | semmle.label | ControlFlowNode for apply_lambda() |
|
||||
| summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | semmle.label | ControlFlowNode for tainted_lambda |
|
||||
| summaries.py:44:25:44:32 | ControlFlowNode for List | semmle.label | ControlFlowNode for List |
|
||||
| summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:45:6:45:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| summaries.py:51:18:51:46 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:51:38:51:45 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:52:6:52:19 | ControlFlowNode for tainted_mapped [List element] | semmle.label | ControlFlowNode for tainted_mapped [List element] |
|
||||
| summaries.py:52:6:52:22 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| summaries.py:57:27:57:63 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:57:55:57:62 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:58:6:58:28 | ControlFlowNode for tainted_mapped_explicit [List element] | semmle.label | ControlFlowNode for tainted_mapped_explicit [List element] |
|
||||
| summaries.py:58:6:58:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| summaries.py:60:26:60:53 | ControlFlowNode for list_map() [List element] | semmle.label | ControlFlowNode for list_map() [List element] |
|
||||
| summaries.py:60:45:60:52 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] |
|
||||
| summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:61:6:61:27 | ControlFlowNode for tainted_mapped_summary [List element] | semmle.label | ControlFlowNode for tainted_mapped_summary [List element] |
|
||||
| summaries.py:61:6:61:30 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| summaries.py:63:16:63:41 | ControlFlowNode for append_to_list() [List element] | semmle.label | ControlFlowNode for append_to_list() [List element] |
|
||||
| summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:64:6:64:17 | ControlFlowNode for tainted_list [List element] | semmle.label | ControlFlowNode for tainted_list [List element] |
|
||||
| summaries.py:64:6:64:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
| summaries.py:67:22:67:39 | ControlFlowNode for json_loads() [List element] | semmle.label | ControlFlowNode for json_loads() [List element] |
|
||||
| summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE |
|
||||
| summaries.py:68:6:68:23 | ControlFlowNode for tainted_resultlist [List element] | semmle.label | ControlFlowNode for tainted_resultlist [List element] |
|
||||
| summaries.py:68:6:68:26 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||
subpaths
|
||||
invalidSpecComponent
|
||||
#select
|
||||
| summaries.py:33:6:33:12 | ControlFlowNode for tainted | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | summaries.py:33:6:33:12 | ControlFlowNode for tainted | $@ | summaries.py:32:20:32:25 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
| summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | summaries.py:37:6:37:19 | ControlFlowNode for tainted_lambda | $@ | summaries.py:36:48:36:53 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
| summaries.py:45:6:45:20 | ControlFlowNode for Subscript | summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | summaries.py:45:6:45:20 | ControlFlowNode for Subscript | $@ | summaries.py:44:26:44:31 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
| summaries.py:52:6:52:22 | ControlFlowNode for Subscript | summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | summaries.py:52:6:52:22 | ControlFlowNode for Subscript | $@ | summaries.py:51:39:51:44 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
| summaries.py:58:6:58:31 | ControlFlowNode for Subscript | summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | summaries.py:58:6:58:31 | ControlFlowNode for Subscript | $@ | summaries.py:57:56:57:61 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
| summaries.py:61:6:61:30 | ControlFlowNode for Subscript | summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | summaries.py:61:6:61:30 | ControlFlowNode for Subscript | $@ | summaries.py:60:46:60:51 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
| summaries.py:64:6:64:20 | ControlFlowNode for Subscript | summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | summaries.py:64:6:64:20 | ControlFlowNode for Subscript | $@ | summaries.py:63:35:63:40 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
||||
| summaries.py:68:6:68:26 | ControlFlowNode for Subscript | summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | summaries.py:68:6:68:26 | ControlFlowNode for Subscript | $@ | summaries.py:67:33:67:38 | ControlFlowNode for SOURCE | ControlFlowNode for SOURCE |
|
|
@ -0,0 +1,68 @@
|
|||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname((__file__))))
|
||||
from testlib import expects
|
||||
|
||||
# These are defined so that we can evaluate the test code.
|
||||
NONSOURCE = "not a source"
|
||||
SOURCE = "source"
|
||||
|
||||
|
||||
def is_source(x):
|
||||
return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j
|
||||
|
||||
|
||||
def SINK(x):
|
||||
if is_source(x):
|
||||
print("OK")
|
||||
else:
|
||||
print("Unexpected flow", x)
|
||||
|
||||
|
||||
def SINK_F(x):
|
||||
if is_source(x):
|
||||
print("Unexpected flow", x)
|
||||
else:
|
||||
print("OK")
|
||||
|
||||
|
||||
# Simple summary
|
||||
tainted = identity(SOURCE)
|
||||
SINK(tainted) # $ flow="SOURCE, l:-1 -> tainted"
|
||||
|
||||
# Lambda summary
|
||||
tainted_lambda = apply_lambda(lambda x: x + 1, SOURCE)
|
||||
SINK(tainted_lambda) # $ flow="SOURCE, l:-1 -> tainted_lambda"
|
||||
|
||||
# A lambda that breaks the flow
|
||||
untainted_lambda = apply_lambda(lambda x: 1, SOURCE)
|
||||
SINK_F(untainted_lambda)
|
||||
|
||||
# Collection summaries
|
||||
tainted_list = reversed([SOURCE])
|
||||
SINK(tainted_list[0]) # $ flow="SOURCE, l:-1 -> tainted_list[0]"
|
||||
|
||||
# Complex summaries
|
||||
def add_colon(x):
|
||||
return x + ":"
|
||||
|
||||
tainted_mapped = list_map(add_colon, [SOURCE])
|
||||
SINK(tainted_mapped[0]) # $ flow="SOURCE, l:-1 -> tainted_mapped[0]"
|
||||
|
||||
def explicit_identity(x):
|
||||
return x
|
||||
|
||||
tainted_mapped_explicit = list_map(explicit_identity, [SOURCE])
|
||||
SINK(tainted_mapped_explicit[0]) # $ flow="SOURCE, l:-1 -> tainted_mapped_explicit[0]"
|
||||
|
||||
tainted_mapped_summary = list_map(identity, [SOURCE])
|
||||
SINK(tainted_mapped_summary[0]) # $ flow="SOURCE, l:-1 -> tainted_mapped_summary[0]"
|
||||
|
||||
tainted_list = append_to_list([], SOURCE)
|
||||
SINK(tainted_list[0]) # $ flow="SOURCE, l:-1 -> tainted_list[0]"
|
||||
|
||||
from json import loads as json_loads
|
||||
tainted_resultlist = json_loads(SOURCE)
|
||||
SINK(tainted_resultlist[0]) # $ flow="SOURCE, l:-1 -> tainted_resultlist[0]"
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.dataflow.new.FlowSummary
|
||||
import DataFlow::PathGraph
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.internal.FlowSummaryImpl
|
||||
import semmle.python.ApiGraphs
|
||||
import experimental.dataflow.testTaintConfig
|
||||
private import TestSummaries
|
||||
|
||||
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
|
||||
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
|
||||
Private::External::invalidSpecComponent(s, c)
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, TestConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink, source, sink, "$@", source, source.toString()
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Configuration to test selected data flow
|
||||
* Sources in the source code are denoted by the special name `SOURCE`,
|
||||
* and sinks are denoted by arguments to the special function `SINK`.
|
||||
* For example, given the test code
|
||||
* ```python
|
||||
* def test():
|
||||
* s = SOURCE
|
||||
* SINK(s)
|
||||
* ```
|
||||
* `SOURCE` will be a source and the second occurrence of `s` will be a sink.
|
||||
*
|
||||
* In order to test literals, alternative sources are defined for each type:
|
||||
*
|
||||
* for | use
|
||||
* ----------
|
||||
* string | `"source"`
|
||||
* integer | `42`
|
||||
* float | `42.0`
|
||||
* complex | `42j` (not supported yet)
|
||||
*/
|
||||
|
||||
private import python
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
class TestConfiguration extends TaintTracking::Configuration {
|
||||
TestConfiguration() { this = "TestConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
|
||||
or
|
||||
node.(DataFlow::CfgNode).getNode().getNode().(StrConst).getS() = "source"
|
||||
or
|
||||
node.(DataFlow::CfgNode).getNode().getNode().(IntegerLiteral).getN() = "42"
|
||||
or
|
||||
node.(DataFlow::CfgNode).getNode().getNode().(FloatLiteral).getN() = "42.0"
|
||||
// No support for complex numbers
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(CallNode call |
|
||||
call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
|
||||
node.(DataFlow::CfgNode).getNode() = call.getAnArg()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
|
||||
|
||||
override int explorationLimit() { result = 5 }
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import python
|
||||
import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import semmle.python.dataflow.new.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import semmle.python.frameworks.data.ModelsAsData
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.dataflow.new.DataFlow
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import python
|
||||
import semmle.python.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import semmle.python.dataflow.new.internal.AccessPathSyntax as AccessPathSyntax
|
||||
import semmle.python.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||
import semmle.python.frameworks.data.ModelsAsData
|
||||
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/**
|
||||
* @name Alert message style violation
|
||||
* @description An alert message that doesn't follow the style guide is harder for end users to digest.
|
||||
* See the style guide here: https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md#alert-messages
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @id ql/alert-message-style-violation
|
||||
* @precision high
|
||||
*/
|
||||
|
||||
import ql
|
||||
|
||||
/** Gets the `index`th part of the select statement. */
|
||||
private AstNode getSelectPart(Select sel, int index) {
|
||||
result =
|
||||
rank[index](AstNode n, Location loc |
|
||||
(
|
||||
n.getParent*() = sel.getExpr(_) and loc = n.getLocation()
|
||||
or
|
||||
// the strings are behind a predicate call.
|
||||
exists(Call c, Predicate target |
|
||||
c.getParent*() = sel.getExpr(_) and loc = c.getLocation()
|
||||
|
|
||||
c.getTarget() = target and
|
||||
(
|
||||
target.getBody().(ComparisonFormula).getAnOperand() = n
|
||||
or
|
||||
exists(ClassPredicate sub | sub.overrides(target) |
|
||||
sub.getBody().(ComparisonFormula).getAnOperand() = n
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
||||
n
|
||||
order by
|
||||
loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn(),
|
||||
loc.getFile().getRelativePath()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string element that is the last part of the message, that doesn't end with a period.
|
||||
*
|
||||
* For example:
|
||||
* ```CodeQL
|
||||
* select foo(), "This is a description" // <- bad
|
||||
*
|
||||
* select foo(), "This is a description." // <- good
|
||||
* ```
|
||||
*/
|
||||
String shouldHaveFullStop(Select sel) {
|
||||
result =
|
||||
max(AstNode str, int i |
|
||||
str.getParent+() = sel.getExpr(1) and str = getSelectPart(sel, i)
|
||||
|
|
||||
str order by i
|
||||
) and
|
||||
not result.getValue().matches("%.") and
|
||||
not result.getValue().matches("%?")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string element that is the first part of the message, that starts with a lower case letter.
|
||||
*
|
||||
* For example:
|
||||
* ```CodeQL
|
||||
* select foo(), "this is a description." // <- bad
|
||||
*
|
||||
* select foo(), "This is a description." // <- good
|
||||
* ```
|
||||
*/
|
||||
String shouldStartCapital(Select sel) {
|
||||
result =
|
||||
min(AstNode str, int i |
|
||||
str.getParent+() = sel.getExpr(1) and str = getSelectPart(sel, i)
|
||||
|
|
||||
str order by i
|
||||
) and
|
||||
result.getValue().regexpMatch("^[a-z].*")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string element that is used in a message that contains "here" or "this location".
|
||||
*
|
||||
* For example:
|
||||
* ```CodeQL
|
||||
* select foo(), "XSS happens here from using a unsafe value." // <- bad
|
||||
*
|
||||
* select foo(), "XSS from using a unsafe value." // <- good
|
||||
* ```
|
||||
*/
|
||||
String avoidHere(string part) {
|
||||
part = ["here", "this location"] and
|
||||
(
|
||||
result.getValue().regexpMatch(".*\\b" + part + "\\b.*") and
|
||||
result = getSelectPart(_, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid using an indefinite article ("a" or "an") in a link text.
|
||||
*
|
||||
* For example:
|
||||
* ```CodeQL
|
||||
* select foo(), "XSS from $@", val, "an unsafe value." // <- bad
|
||||
*
|
||||
* select foo(), "XSS from a $@", val, "unsafe value." // <- good
|
||||
* ```
|
||||
*
|
||||
* See https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html for the W3C guideline on link text. a
|
||||
*/
|
||||
String avoidArticleInLinkText(Select sel) {
|
||||
result = sel.getExpr((any(int i | i > 1))) and
|
||||
result = getSelectPart(sel, _) and
|
||||
result.getValue().regexpMatch("a|an .*")
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't quote substitutions in a message.
|
||||
*
|
||||
* For example:
|
||||
* ```CodeQL
|
||||
* select foo(), "XSS from '$@'", val, "an unsafe value." // <- bad
|
||||
*
|
||||
* select foo(), "XSS from $@", val, "an unsafe value." // <- good
|
||||
* ```
|
||||
*/
|
||||
String dontQuoteSubstitutions(Select sel) {
|
||||
result = getSelectPart(sel, _) and
|
||||
result.getValue().matches(["%'$@'%", "%\"$@\"%"])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the kind of the path-query represented by `sel`.
|
||||
* Either "data" for a dataflow query or "taint" for a taint-tracking query.
|
||||
*/
|
||||
private string getQueryKind(Select sel) {
|
||||
exists(TypeExpr sup |
|
||||
sup = sel.getVarDecl(_).getType().(ClassType).getDeclaration().getASuperType() and
|
||||
sup.getResolvedType().(ClassType).getName() = "Configuration"
|
||||
|
|
||||
result = "data" and
|
||||
sup.getModule().getName() = "DataFlow"
|
||||
or
|
||||
result = "taint" and
|
||||
sup.getModule().getName() = "TaintTracking"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string element from a message that uses the wrong phrase for a path query.
|
||||
* A dataflow query should use "flows to" and a taint-tracking query should use "depends on".
|
||||
*/
|
||||
String wrongFlowsPhrase(Select sel, string kind) {
|
||||
result = getSelectPart(sel, _) and
|
||||
kind = getQueryKind(sel) and
|
||||
(
|
||||
kind = "data" and
|
||||
result.getValue().matches(["% depends %", "% depend %"])
|
||||
or
|
||||
kind = "taint" and
|
||||
result.getValue().matches(["% flows to %", "% flow to %"])
|
||||
)
|
||||
}
|
||||
|
||||
from AstNode node, string msg
|
||||
where
|
||||
not node.getLocation().getFile().getAbsolutePath().matches("%/test/%") and
|
||||
(
|
||||
node = shouldHaveFullStop(_) and
|
||||
msg = "Alert message should end with a full stop."
|
||||
or
|
||||
node = shouldStartCapital(_) and
|
||||
msg = "Alert message should start with a capital letter."
|
||||
or
|
||||
exists(string part | node = avoidHere(part) |
|
||||
part = "here" and
|
||||
msg =
|
||||
"Try to use a descriptive phrase instead of \"here\". Use \"this location\" if you can't get around mentioning the current location."
|
||||
or
|
||||
part = "this location" and
|
||||
msg = "Try to more descriptive phrase instead of \"this location\" if possible."
|
||||
)
|
||||
or
|
||||
node = avoidArticleInLinkText(_) and
|
||||
msg = "Avoid starting a link text with an indefinite article."
|
||||
or
|
||||
node = dontQuoteSubstitutions(_) and
|
||||
msg = "Don't quote substitutions in alert messages."
|
||||
or
|
||||
node = wrongFlowsPhrase(_, "data") and
|
||||
msg = "Use \"flows to\" instead of \"depends on\" in data flow queries."
|
||||
or
|
||||
node = wrongFlowsPhrase(_, "taint") and
|
||||
msg = "Use \"depends on\" instead of \"flows to\" in taint tracking queries."
|
||||
)
|
||||
select node, msg
|
|
@ -63,14 +63,6 @@ module RegExpFlags {
|
|||
root.getLiteral().isIgnoreCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flags for `root`, or the empty string if `root` has no flags.
|
||||
*/
|
||||
string getFlags(RegExpTerm root) {
|
||||
root.isRootTerm() and
|
||||
result = root.getLiteral().getFlags()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `root` has the `s` flag for multi-line matching.
|
||||
*/
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче