Compare commits

..

20 Commits

Author SHA1 Message Date
yoff
d26102b263 Merge pull request #21920 from github/yoff/python-flow-py-namespace
Python: qualify Flow.qll's AST references with Py:: prefix
2026-06-23 13:20:26 +02:00
yoff
7d95024487 Apply suggestions from code review
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-23 12:36:53 +02:00
Copilot
06fa46f664 Python: qualify Flow.qll's AST references with Py:: prefix
Preparatory refactor for the shared-CFG dataflow migration. Switches
'import python' to 'import python as Py' inside Flow.qll, and qualifies
every AST-class reference (Expr, Bytes, Dict, AssignExpr, Compare,
Module, Scope, Call, Attribute, SsaVariable, AugAssign, etc.) with the
Py:: prefix.

Flow.qll's own CFG types (ControlFlowNode, BasicBlock, CallNode,
NameNode, DefinitionNode, CompareNode, ...) keep their unqualified
names — they remain the public CFG API exported from this file.

This is a semantic noop: the qualification was applied mechanically by
script and no name resolution changes. Verified by:
- All 361 lib/ + src/ queries compile clean.
- All 186 ControlFlow + PointsTo + dataflow library-tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-23 12:36:53 +02:00
Geoffrey White
f6dce466a0 Merge pull request #22009 from geoffw0/rust-crypto
Rust: Additional test cases for rust/weak-sensitive-data-hashing
2026-06-23 10:53:45 +01:00
Idriss Riouak
ec91865a7f Merge pull request #22030 from github/idrissrio/cpp/update-stats-file
C/C++: Update stats file
2026-06-23 10:26:52 +02:00
Owen Mansel-Chan
f0576046b1 Merge pull request #22027 from owen-mc/go/improve-tests
Go: Improve two tests
2026-06-22 17:19:40 +01:00
Geoffrey White
9e0e1bde28 Rust: Use Copilot suggested comment phrasing. 2026-06-22 16:12:54 +01:00
Geoffrey White
8c24acc99d Fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-22 16:10:57 +01:00
yoff
32f7c541ae Merge pull request #21919 from github/yoff/python-remove-getAFlowNode
Python: deprecate AstNode.getAFlowNode() and rewrite callers
2026-06-22 15:35:52 +02:00
yoff
1a9bb2416a Python: deprecate Function.getAReturnValueFlowNode() and rewrite internal callers
Follow-up to the getAFlowNode deprecation in the same PR: same AST→legacy-CFG
bridge pattern. The 11 internal call sites (across objects/, types/,
frameworks/, and TypeTrackingImpl) are rewritten to bind a `Return ret`
explicitly, then constrain via `ret.getScope() = f and n.getNode() = ret.getValue()`.

The predicate itself is preserved with a deprecation note so external
users do not experience churn.

Semantic noop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-22 14:55:19 +02:00
Copilot
717ff62d70 Python: deprecate AstNode.getAFlowNode() and rewrite internal callers
Preparatory refactor for the shared-CFG dataflow migration.

Deprecates the AstNode.getAFlowNode() cached predicate on the public
Python QL API and rewrites all ~140 internal callers across lib/, src/,
test/, and tools/ from `expr.getAFlowNode() = cfgNode` to
`cfgNode.getNode() = expr`, using ControlFlowNode.getNode() which
already exists in Flow.qll.

The predicate itself is preserved (with a deprecation note pointing at
the new pattern) so external users do not experience churn — they can
migrate at their own pace and the AST/CFG hierarchies still get the
intended untangling once the deprecation eventually elapses.

Semantic noop verified by:
- All 361 lib/ + src/ queries compile clean.
- All 122 ControlFlow + PointsTo library-tests pass.
- All 64 dataflow library-tests pass.
- All 113 Variables/Exceptions/Expressions/Statements/Functions/Imports/
  Security/CWE-798/ModificationOfParameterWithDefault query-tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-22 14:55:19 +02:00
yoff
8179bffe64 Merge pull request #21930 from github/yoff/python-dataflow-noop-simplifications
Python: inline init_module_submodule_defn into ImportResolution
2026-06-22 14:50:39 +02:00
idrissrio
0a41157d77 C/C++: update stats file 2026-06-22 10:27:21 +02:00
Owen Mansel-Chan
07cf89568f Test CFG for function epilogue (read-result nodes and calls to defered functions) 2026-06-20 22:04:45 +01:00
Owen Mansel-Chan
42ebe56023 Make all lines in logging tests reachable 2026-06-20 22:04:43 +01:00
Geoffrey White
721070a191 Rust: Make the Seed case a tiny bit more realistic. 2026-06-18 23:43:18 +01:00
Geoffrey White
b86cb6df63 Rust: Additional test cases for weak sensitive data hashing. 2026-06-18 23:32:38 +01:00
Geoffrey White
3aaeb68553 Rust: Make the new test inline expectations. 2026-06-18 22:57:15 +01:00
Geoffrey White
e8923b7688 Rust: Output cryptographic operations in the weak sensitive data hashing query test. 2026-06-18 17:07:28 +01:00
yoff
ac5fa629ef Python: inline init_module_submodule_defn into ImportResolution
The new-dataflow ImportResolution module only used
semmle.python.essa.SsaDefinitions for the 5-line helper predicate
SsaSource::init_module_submodule_defn. Inline it locally and drop the
dependency on legacy SsaDefinitions. This is the only remaining direct
import of semmle.python.essa.* in the new dataflow stack, so dropping
it makes the layering cleaner.

Semantic noop on the current SSA: SsaSourceVariable.getName() and
GlobalVariable.getId() both project the same DB column
(variable(_,_,result)), and the old call's 'init.getEntryNode() = f'
join was just constraining init = package via Scope.getEntryNode()'s
functional uniqueness. RA dump of accesses.ql confirms only the
expected predicate-rename shuffle; all 70 dataflow + ApiGraphs library
tests pass.

This factors out commit 8cab5a20f2 from the larger shared-CFG
migration #21925.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-02 08:24:17 +00:00
92 changed files with 4850 additions and 4212 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -121,13 +121,6 @@ private module Cached {
result = getAChildExpr(parent) result = getAChildExpr(parent)
or or
result = parent.getAChildStmt() result = parent.getAChildStmt()
or
result =
any(TypeMention tm |
tm.getTarget() = parent
or
tm.getParent+().getTarget() = parent
)
} }
private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) { private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) {

View File

@@ -6,7 +6,6 @@ import Generics
import Location import Location
import Namespace import Namespace
import Property import Property
import semmle.code.csharp.controlflow.ControlFlowElement
private import Conversion private import Conversion
private import semmle.code.csharp.metrics.Coupling private import semmle.code.csharp.metrics.Coupling
private import TypeRef private import TypeRef
@@ -1287,7 +1286,7 @@ class TupleType extends ValueType, @tuple_type {
* A type mention, that is, any mention of a type in a source code file. * A type mention, that is, any mention of a type in a source code file.
* For example, `int` is mentioned in `int M() { return 1; }`. * For example, `int` is mentioned in `int M() { return 1; }`.
*/ */
class TypeMention extends ControlFlowElement, @type_mention { class TypeMention extends @type_mention {
Type type; Type type;
@type_mention_parent parent; @type_mention_parent parent;
@@ -1320,13 +1319,13 @@ class TypeMention extends ControlFlowElement, @type_mention {
* } * }
* ``` * ```
*/ */
override TypeMention getParent() { result = parent } TypeMention getParent() { result = parent }
/** Gets a textual representation of this type mention. */ /** Gets a textual representation of this type mention. */
override string toString() { result = type.toString() } string toString() { result = type.toString() }
/** Gets the location of this type mention. */ /** Gets the location of this type mention. */
override Location getALocation() { type_mention_location(this, result) } Location getLocation() { type_mention_location(this, result) }
} }
/** /**

View File

@@ -20,7 +20,7 @@ class ControlFlowElementOrCallable extends ExprOrStmtParent, TControlFlowElement
*/ */
class ControlFlowElement extends ControlFlowElementOrCallable, @control_flow_element { class ControlFlowElement extends ControlFlowElementOrCallable, @control_flow_element {
/** Gets the enclosing callable of this element, if any. */ /** Gets the enclosing callable of this element, if any. */
Callable getEnclosingCallable() { enclosingCallable(this, result) } Callable getEnclosingCallable() { none() }
/** Gets the assembly that this element was compiled into. */ /** Gets the assembly that this element was compiled into. */
Assembly getAssembly() { Assembly getAssembly() {

View File

@@ -219,7 +219,7 @@ overlayChangedFiles(
/** ELEMENTS **/ /** ELEMENTS **/
@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration @element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
| @using_directive | @type_parameter_constraints | @type_mention | @externalDataElement | @using_directive | @type_parameter_constraints | @externalDataElement
| @xmllocatable | @asp_element | @namespace | @preprocessor_directive; | @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
@declaration = @callable | @generic | @assignable | @namespace; @declaration = @callable | @generic | @assignable | @namespace;
@@ -1369,7 +1369,7 @@ compiler_generated(unique int id: @element ref);
/** CONTROL/DATA FLOW **/ /** CONTROL/DATA FLOW **/
@control_flow_element = @stmt | @expr | @parameter | @type_mention; @control_flow_element = @stmt | @expr | @parameter;
/* XML Files */ /* XML Files */

View File

@@ -13,7 +13,7 @@ func logSomething(entry *logrus.Entry) {
entry.Traceln(text) // $ logger=text entry.Traceln(text) // $ logger=text
} }
func logrusCalls() { func logrusCalls(selector int) {
err := errors.New("Error") err := errors.New("Error")
var fields logrus.Fields = nil var fields logrus.Fields = nil
var fn logrus.LogFunction = nil var fn logrus.LogFunction = nil
@@ -27,11 +27,15 @@ func logrusCalls() {
tmp = logrus.WithFields(fields) // $ logger=fields tmp = logrus.WithFields(fields) // $ logger=fields
logSomething(tmp) logSomething(tmp)
logrus.Error(text) // $ logger=text logrus.Error(text) // $ logger=text
logrus.Fatalf(fmt, text) // $ logger=fmt logger=text logrus.Infof(fmt, text) // $ logger=fmt logger=text
logrus.Panicln(text) // $ logger=text if selector == 0 {
logrus.Infof(fmt, text) // $ logger=fmt logger=text logrus.Fatalf(fmt, text) // $ logger=fmt logger=text
logrus.FatalFn(fn) // $ logger=fn } else if selector == 1 {
logrus.Panicln(text) // $ logger=text
} else if selector == 2 {
logrus.FatalFn(fn) // $ logger=fn
}
// components corresponding to the format specifier "%T" are not considered vulnerable // components corresponding to the format specifier "%T" are not considered vulnerable
logrus.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v logrus.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v

View File

@@ -8,6 +8,6 @@ var v []byte
func main() { func main() {
glogTest(len(v)) glogTest(len(v))
stdlib() stdlib(len(v))
slogTest() slogTest()
} }

View File

@@ -4,37 +4,69 @@ import (
"log" "log"
) )
func stdlib() { func stdlib(selector int) {
var logger log.Logger var logger log.Logger
logger.SetPrefix("prefix: ") logger.SetPrefix("prefix: ")
logger.Fatal(text) // $ logger=text switch selector {
logger.Fatalf(fmt, text) // $ logger=fmt logger=text case 0:
logger.Fatalln(text) // $ logger=text logger.Fatal(text) // $ logger=text
logger.Panic(text) // $ logger=text case 1:
logger.Panicf(fmt, text) // $ logger=fmt logger=text logger.Fatalf(fmt, text) // $ logger=fmt logger=text
logger.Panicln(text) // $ logger=text case 2:
logger.Print(text) // $ logger=text logger.Fatalln(text) // $ logger=text
logger.Printf(fmt, text) // $ logger=fmt logger=text case 3:
logger.Println(text) // $ logger=text logger.Panic(text) // $ logger=text
case 4:
logger.Panicf(fmt, text) // $ logger=fmt logger=text
case 5:
logger.Panicln(text) // $ logger=text
case 6:
logger.Print(text) // $ logger=text
case 7:
logger.Printf(fmt, text) // $ logger=fmt logger=text
case 8:
logger.Println(text) // $ logger=text
}
// components corresponding to the format specifier "%T" are not considered vulnerable // components corresponding to the format specifier "%T" are not considered vulnerable
logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v switch selector {
logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v case 9:
logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 10:
logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 11:
logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
}
log.SetPrefix("prefix: ") log.SetPrefix("prefix: ")
log.Fatal(text) // $ logger=text switch selector {
log.Fatalf(fmt, text) // $ logger=fmt logger=text case 12:
log.Fatalln(text) // $ logger=text log.Fatal(text) // $ logger=text
log.Panic(text) // $ logger=text case 13:
log.Panicf(fmt, text) // $ logger=fmt logger=text log.Fatalf(fmt, text) // $ logger=fmt logger=text
log.Panicln(text) // $ logger=text case 14:
log.Print(text) // $ logger=text log.Fatalln(text) // $ logger=text
log.Printf(fmt, text) // $ logger=fmt logger=text case 15:
log.Println(text) // $ logger=text log.Panic(text) // $ logger=text
case 16:
log.Panicf(fmt, text) // $ logger=fmt logger=text
case 17:
log.Panicln(text) // $ logger=text
case 18:
log.Print(text) // $ logger=text
case 19:
log.Printf(fmt, text) // $ logger=fmt logger=text
case 20:
log.Println(text) // $ logger=text
}
// components corresponding to the format specifier "%T" are not considered vulnerable // components corresponding to the format specifier "%T" are not considered vulnerable
log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v switch selector {
log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v case 21:
log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 22:
log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 23:
log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
}
} }

View File

@@ -34,6 +34,265 @@
| DuplicateSwitchCase.go:16:1:16:14 | function declaration | DuplicateSwitchCase.go:0:0:0:0 | exit | | DuplicateSwitchCase.go:16:1:16:14 | function declaration | DuplicateSwitchCase.go:0:0:0:0 | exit |
| DuplicateSwitchCase.go:16:6:16:9 | skip | DuplicateSwitchCase.go:16:1:16:14 | function declaration | | DuplicateSwitchCase.go:16:6:16:9 | skip | DuplicateSwitchCase.go:16:1:16:14 | function declaration |
| DuplicateSwitchCase.go:16:13:16:14 | skip | DuplicateSwitchCase.go:16:1:16:14 | exit | | DuplicateSwitchCase.go:16:13:16:14 | skip | DuplicateSwitchCase.go:16:1:16:14 | exit |
| epilogues.go:0:0:0:0 | entry | epilogues.go:3:1:3:12 | skip |
| epilogues.go:3:1:3:12 | skip | epilogues.go:8:1:10:1 | skip |
| epilogues.go:8:1:10:1 | skip | epilogues.go:12:21:12:23 | skip |
| epilogues.go:12:1:14:1 | entry | epilogues.go:12:7:12:7 | argument corresponding to l |
| epilogues.go:12:1:14:1 | function declaration | epilogues.go:16:20:16:27 | skip |
| epilogues.go:12:7:12:7 | argument corresponding to l | epilogues.go:12:7:12:7 | initialization of l |
| epilogues.go:12:7:12:7 | initialization of l | epilogues.go:12:25:12:27 | argument corresponding to msg |
| epilogues.go:12:21:12:23 | skip | epilogues.go:12:1:14:1 | function declaration |
| epilogues.go:12:25:12:27 | argument corresponding to msg | epilogues.go:12:25:12:27 | initialization of msg |
| epilogues.go:12:25:12:27 | initialization of msg | epilogues.go:12:37:12:40 | argument corresponding to code |
| epilogues.go:12:37:12:40 | argument corresponding to code | epilogues.go:12:37:12:40 | initialization of code |
| epilogues.go:12:37:12:40 | initialization of code | epilogues.go:13:2:13:12 | selection of Println |
| epilogues.go:13:2:13:12 | selection of Println | epilogues.go:13:14:13:14 | l |
| epilogues.go:13:2:13:33 | call to Println | epilogues.go:12:1:14:1 | exit |
| epilogues.go:13:14:13:14 | implicit dereference | epilogues.go:12:1:14:1 | exit |
| epilogues.go:13:14:13:14 | implicit dereference | epilogues.go:13:14:13:21 | selection of prefix |
| epilogues.go:13:14:13:14 | l | epilogues.go:13:14:13:14 | implicit dereference |
| epilogues.go:13:14:13:21 | selection of prefix | epilogues.go:13:24:13:26 | msg |
| epilogues.go:13:24:13:26 | msg | epilogues.go:13:29:13:32 | code |
| epilogues.go:13:29:13:32 | code | epilogues.go:13:2:13:33 | call to Println |
| epilogues.go:16:1:18:1 | entry | epilogues.go:16:7:16:7 | argument corresponding to l |
| epilogues.go:16:1:18:1 | function declaration | epilogues.go:23:6:23:15 | skip |
| epilogues.go:16:7:16:7 | argument corresponding to l | epilogues.go:16:7:16:7 | initialization of l |
| epilogues.go:16:7:16:7 | initialization of l | epilogues.go:16:29:16:31 | argument corresponding to msg |
| epilogues.go:16:20:16:27 | skip | epilogues.go:16:1:18:1 | function declaration |
| epilogues.go:16:29:16:31 | argument corresponding to msg | epilogues.go:16:29:16:31 | initialization of msg |
| epilogues.go:16:29:16:31 | initialization of msg | epilogues.go:17:2:17:12 | selection of Println |
| epilogues.go:17:2:17:12 | selection of Println | epilogues.go:17:14:17:14 | l |
| epilogues.go:17:2:17:27 | call to Println | epilogues.go:16:1:18:1 | exit |
| epilogues.go:17:14:17:14 | l | epilogues.go:17:14:17:21 | selection of prefix |
| epilogues.go:17:14:17:21 | selection of prefix | epilogues.go:17:24:17:26 | msg |
| epilogues.go:17:24:17:26 | msg | epilogues.go:17:2:17:27 | call to Println |
| epilogues.go:23:1:27:1 | entry | epilogues.go:24:5:24:5 | skip |
| epilogues.go:23:1:27:1 | function declaration | epilogues.go:31:6:31:13 | skip |
| epilogues.go:23:6:23:15 | skip | epilogues.go:23:1:27:1 | function declaration |
| epilogues.go:24:5:24:5 | assignment to r | epilogues.go:24:21:24:21 | r |
| epilogues.go:24:5:24:5 | skip | epilogues.go:24:10:24:16 | recover |
| epilogues.go:24:10:24:16 | recover | epilogues.go:24:10:24:18 | call to recover |
| epilogues.go:24:10:24:18 | call to recover | epilogues.go:24:5:24:5 | assignment to r |
| epilogues.go:24:21:24:21 | r | epilogues.go:24:26:24:28 | nil |
| epilogues.go:24:21:24:28 | ...!=... | epilogues.go:23:1:27:1 | exit |
| epilogues.go:24:21:24:28 | ...!=... | epilogues.go:24:21:24:28 | ...!=... is false |
| epilogues.go:24:21:24:28 | ...!=... | epilogues.go:24:21:24:28 | ...!=... is true |
| epilogues.go:24:21:24:28 | ...!=... is false | epilogues.go:23:1:27:1 | exit |
| epilogues.go:24:21:24:28 | ...!=... is true | epilogues.go:25:3:25:13 | selection of Println |
| epilogues.go:24:26:24:28 | nil | epilogues.go:24:21:24:28 | ...!=... |
| epilogues.go:25:3:25:13 | selection of Println | epilogues.go:25:15:25:26 | "recovered:" |
| epilogues.go:25:3:25:30 | call to Println | epilogues.go:23:1:27:1 | exit |
| epilogues.go:25:15:25:26 | "recovered:" | epilogues.go:25:29:25:29 | r |
| epilogues.go:25:29:25:29 | r | epilogues.go:25:3:25:30 | call to Println |
| epilogues.go:31:1:33:1 | entry | epilogues.go:31:15:31:15 | argument corresponding to x |
| epilogues.go:31:1:33:1 | function declaration | epilogues.go:36:6:36:12 | skip |
| epilogues.go:31:6:31:13 | skip | epilogues.go:31:1:33:1 | function declaration |
| epilogues.go:31:15:31:15 | argument corresponding to x | epilogues.go:31:15:31:15 | initialization of x |
| epilogues.go:31:15:31:15 | initialization of x | epilogues.go:32:9:32:9 | x |
| epilogues.go:32:2:32:13 | return statement | epilogues.go:31:1:33:1 | exit |
| epilogues.go:32:9:32:9 | x | epilogues.go:32:13:32:13 | 2 |
| epilogues.go:32:9:32:13 | ...*... | epilogues.go:32:2:32:13 | return statement |
| epilogues.go:32:13:32:13 | 2 | epilogues.go:32:9:32:13 | ...*... |
| epilogues.go:36:1:38:1 | entry | epilogues.go:37:2:37:12 | selection of Println |
| epilogues.go:36:1:38:1 | function declaration | epilogues.go:42:6:42:18 | skip |
| epilogues.go:36:6:36:12 | skip | epilogues.go:36:1:38:1 | function declaration |
| epilogues.go:37:2:37:12 | selection of Println | epilogues.go:37:14:37:19 | "void" |
| epilogues.go:37:2:37:20 | call to Println | epilogues.go:36:1:38:1 | exit |
| epilogues.go:37:14:37:19 | "void" | epilogues.go:37:2:37:20 | call to Println |
| epilogues.go:42:1:48:1 | entry | epilogues.go:42:20:42:20 | argument corresponding to x |
| epilogues.go:42:1:48:1 | function declaration | epilogues.go:51:6:51:21 | skip |
| epilogues.go:42:6:42:18 | skip | epilogues.go:42:1:48:1 | function declaration |
| epilogues.go:42:20:42:20 | argument corresponding to x | epilogues.go:42:20:42:20 | initialization of x |
| epilogues.go:42:20:42:20 | initialization of x | epilogues.go:42:28:42:33 | zero value for result |
| epilogues.go:42:28:42:33 | implicit read of result | epilogues.go:42:40:42:42 | implicit read of err |
| epilogues.go:42:28:42:33 | initialization of result | epilogues.go:42:40:42:42 | zero value for err |
| epilogues.go:42:28:42:33 | zero value for result | epilogues.go:42:28:42:33 | initialization of result |
| epilogues.go:42:40:42:42 | implicit read of err | epilogues.go:42:1:48:1 | exit |
| epilogues.go:42:40:42:42 | initialization of err | epilogues.go:43:5:43:5 | x |
| epilogues.go:42:40:42:42 | zero value for err | epilogues.go:42:40:42:42 | initialization of err |
| epilogues.go:43:5:43:5 | x | epilogues.go:43:9:43:9 | 0 |
| epilogues.go:43:5:43:9 | ...<... | epilogues.go:43:5:43:9 | ...<... is false |
| epilogues.go:43:5:43:9 | ...<... | epilogues.go:43:5:43:9 | ...<... is true |
| epilogues.go:43:5:43:9 | ...<... is false | epilogues.go:47:9:47:9 | x |
| epilogues.go:43:5:43:9 | ...<... is true | epilogues.go:44:3:44:8 | skip |
| epilogues.go:43:9:43:9 | 0 | epilogues.go:43:5:43:9 | ...<... |
| epilogues.go:44:3:44:8 | assignment to result | epilogues.go:45:3:45:8 | return statement |
| epilogues.go:44:3:44:8 | skip | epilogues.go:44:13:44:13 | x |
| epilogues.go:44:12:44:13 | -... | epilogues.go:44:3:44:8 | assignment to result |
| epilogues.go:44:13:44:13 | x | epilogues.go:44:12:44:13 | -... |
| epilogues.go:45:3:45:8 | return statement | epilogues.go:42:28:42:33 | implicit read of result |
| epilogues.go:47:2:47:14 | return statement | epilogues.go:42:28:42:33 | implicit read of result |
| epilogues.go:47:9:47:9 | implicit write of result | epilogues.go:47:12:47:14 | nil |
| epilogues.go:47:9:47:9 | x | epilogues.go:47:9:47:9 | implicit write of result |
| epilogues.go:47:12:47:14 | implicit write of err | epilogues.go:47:2:47:14 | return statement |
| epilogues.go:47:12:47:14 | nil | epilogues.go:47:12:47:14 | implicit write of err |
| epilogues.go:51:1:54:1 | entry | epilogues.go:51:23:51:23 | argument corresponding to x |
| epilogues.go:51:1:54:1 | function declaration | epilogues.go:59:6:59:25 | skip |
| epilogues.go:51:6:51:21 | skip | epilogues.go:51:1:54:1 | function declaration |
| epilogues.go:51:23:51:23 | argument corresponding to x | epilogues.go:51:23:51:23 | initialization of x |
| epilogues.go:51:23:51:23 | initialization of x | epilogues.go:51:31:51:31 | zero value for n |
| epilogues.go:51:31:51:31 | implicit read of n | epilogues.go:51:1:54:1 | exit |
| epilogues.go:51:31:51:31 | initialization of n | epilogues.go:52:2:52:2 | skip |
| epilogues.go:51:31:51:31 | zero value for n | epilogues.go:51:31:51:31 | initialization of n |
| epilogues.go:52:2:52:2 | assignment to n | epilogues.go:53:2:53:7 | return statement |
| epilogues.go:52:2:52:2 | skip | epilogues.go:52:6:52:6 | x |
| epilogues.go:52:6:52:6 | x | epilogues.go:52:10:52:10 | 1 |
| epilogues.go:52:6:52:10 | ...+... | epilogues.go:52:2:52:2 | assignment to n |
| epilogues.go:52:10:52:10 | 1 | epilogues.go:52:6:52:10 | ...+... |
| epilogues.go:53:2:53:7 | return statement | epilogues.go:51:31:51:31 | implicit read of n |
| epilogues.go:59:1:62:1 | entry | epilogues.go:59:27:59:27 | argument corresponding to l |
| epilogues.go:59:1:62:1 | function declaration | epilogues.go:66:6:66:26 | skip |
| epilogues.go:59:6:59:25 | skip | epilogues.go:59:1:62:1 | function declaration |
| epilogues.go:59:27:59:27 | argument corresponding to l | epilogues.go:59:27:59:27 | initialization of l |
| epilogues.go:59:27:59:27 | initialization of l | epilogues.go:59:41:59:45 | argument corresponding to items |
| epilogues.go:59:41:59:45 | argument corresponding to items | epilogues.go:59:41:59:45 | initialization of items |
| epilogues.go:59:41:59:45 | initialization of items | epilogues.go:60:8:60:8 | l |
| epilogues.go:60:2:60:33 | defer statement | epilogues.go:61:2:61:12 | selection of Println |
| epilogues.go:60:8:60:8 | l | epilogues.go:60:8:60:12 | selection of log |
| epilogues.go:60:8:60:12 | selection of log | epilogues.go:60:14:60:20 | "count" |
| epilogues.go:60:8:60:33 | call to log | epilogues.go:59:1:62:1 | exit |
| epilogues.go:60:14:60:20 | "count" | epilogues.go:60:23:60:25 | len |
| epilogues.go:60:23:60:25 | len | epilogues.go:60:27:60:31 | items |
| epilogues.go:60:23:60:32 | call to len | epilogues.go:60:2:60:33 | defer statement |
| epilogues.go:60:27:60:31 | items | epilogues.go:60:23:60:32 | call to len |
| epilogues.go:61:2:61:12 | selection of Println | epilogues.go:61:14:61:25 | "processing" |
| epilogues.go:61:2:61:38 | call to Println | epilogues.go:60:8:60:33 | call to log |
| epilogues.go:61:14:61:25 | "processing" | epilogues.go:61:28:61:30 | len |
| epilogues.go:61:28:61:30 | len | epilogues.go:61:32:61:36 | items |
| epilogues.go:61:28:61:37 | call to len | epilogues.go:61:2:61:38 | call to Println |
| epilogues.go:61:32:61:36 | items | epilogues.go:61:28:61:37 | call to len |
| epilogues.go:66:1:71:1 | entry | epilogues.go:66:28:66:33 | argument corresponding to prefix |
| epilogues.go:66:1:71:1 | function declaration | epilogues.go:77:6:77:20 | skip |
| epilogues.go:66:6:66:26 | skip | epilogues.go:66:1:71:1 | function declaration |
| epilogues.go:66:28:66:33 | argument corresponding to prefix | epilogues.go:66:28:66:33 | initialization of prefix |
| epilogues.go:66:28:66:33 | initialization of prefix | epilogues.go:67:2:67:2 | skip |
| epilogues.go:67:2:67:2 | assignment to l | epilogues.go:68:8:68:8 | l |
| epilogues.go:67:2:67:2 | skip | epilogues.go:67:7:67:31 | struct literal |
| epilogues.go:67:7:67:31 | struct literal | epilogues.go:67:25:67:30 | prefix |
| epilogues.go:67:17:67:30 | init of key-value pair | epilogues.go:67:2:67:2 | assignment to l |
| epilogues.go:67:25:67:30 | prefix | epilogues.go:67:17:67:30 | init of key-value pair |
| epilogues.go:68:2:68:24 | defer statement | epilogues.go:69:10:69:10 | l |
| epilogues.go:68:8:68:8 | l | epilogues.go:68:8:68:17 | selection of logValue |
| epilogues.go:68:8:68:17 | selection of logValue | epilogues.go:68:19:68:23 | "bye" |
| epilogues.go:68:8:68:24 | call to logValue | epilogues.go:66:1:71:1 | exit |
| epilogues.go:68:19:68:23 | "bye" | epilogues.go:68:2:68:24 | defer statement |
| epilogues.go:69:2:69:25 | defer statement | epilogues.go:70:2:70:12 | selection of Println |
| epilogues.go:69:8:69:15 | selection of log | epilogues.go:69:17:69:21 | "ptr" |
| epilogues.go:69:8:69:25 | call to log | epilogues.go:68:8:68:24 | call to logValue |
| epilogues.go:69:9:69:10 | &... | epilogues.go:69:8:69:15 | selection of log |
| epilogues.go:69:10:69:10 | l | epilogues.go:69:9:69:10 | &... |
| epilogues.go:69:17:69:21 | "ptr" | epilogues.go:69:24:69:24 | 7 |
| epilogues.go:69:24:69:24 | 7 | epilogues.go:69:2:69:25 | defer statement |
| epilogues.go:70:2:70:12 | selection of Println | epilogues.go:70:14:70:19 | "body" |
| epilogues.go:70:2:70:20 | call to Println | epilogues.go:69:8:69:25 | call to log |
| epilogues.go:70:14:70:19 | "body" | epilogues.go:70:2:70:20 | call to Println |
| epilogues.go:77:1:82:1 | entry | epilogues.go:77:22:77:22 | argument corresponding to x |
| epilogues.go:77:1:82:1 | function declaration | epilogues.go:87:6:87:20 | skip |
| epilogues.go:77:6:77:20 | skip | epilogues.go:77:1:82:1 | function declaration |
| epilogues.go:77:22:77:22 | argument corresponding to x | epilogues.go:77:22:77:22 | initialization of x |
| epilogues.go:77:22:77:22 | initialization of x | epilogues.go:78:8:80:2 | function literal |
| epilogues.go:78:2:80:15 | defer statement | epilogues.go:81:2:81:12 | selection of Println |
| epilogues.go:78:8:80:2 | entry | epilogues.go:78:13:78:17 | argument corresponding to label |
| epilogues.go:78:8:80:2 | function literal | epilogues.go:80:4:80:9 | "done" |
| epilogues.go:78:8:80:15 | function call | epilogues.go:77:1:82:1 | exit |
| epilogues.go:78:13:78:17 | argument corresponding to label | epilogues.go:78:13:78:17 | initialization of label |
| epilogues.go:78:13:78:17 | initialization of label | epilogues.go:78:27:78:27 | argument corresponding to n |
| epilogues.go:78:27:78:27 | argument corresponding to n | epilogues.go:78:27:78:27 | initialization of n |
| epilogues.go:78:27:78:27 | initialization of n | epilogues.go:79:3:79:13 | selection of Println |
| epilogues.go:79:3:79:13 | selection of Println | epilogues.go:79:15:79:19 | label |
| epilogues.go:79:3:79:23 | call to Println | epilogues.go:78:8:80:2 | exit |
| epilogues.go:79:15:79:19 | label | epilogues.go:79:22:79:22 | n |
| epilogues.go:79:22:79:22 | n | epilogues.go:79:3:79:23 | call to Println |
| epilogues.go:80:4:80:9 | "done" | epilogues.go:80:12:80:12 | x |
| epilogues.go:80:12:80:12 | x | epilogues.go:80:14:80:14 | 1 |
| epilogues.go:80:12:80:14 | ...+... | epilogues.go:78:2:80:15 | defer statement |
| epilogues.go:80:14:80:14 | 1 | epilogues.go:80:12:80:14 | ...+... |
| epilogues.go:81:2:81:12 | selection of Println | epilogues.go:81:14:81:19 | "body" |
| epilogues.go:81:2:81:23 | call to Println | epilogues.go:78:8:80:15 | function call |
| epilogues.go:81:14:81:19 | "body" | epilogues.go:81:22:81:22 | x |
| epilogues.go:81:22:81:22 | x | epilogues.go:81:2:81:23 | call to Println |
| epilogues.go:87:1:98:1 | entry | epilogues.go:87:22:87:22 | argument corresponding to x |
| epilogues.go:87:1:98:1 | function declaration | epilogues.go:102:6:102:24 | skip |
| epilogues.go:87:6:87:20 | skip | epilogues.go:87:1:98:1 | function declaration |
| epilogues.go:87:22:87:22 | argument corresponding to x | epilogues.go:87:22:87:22 | initialization of x |
| epilogues.go:87:22:87:22 | initialization of x | epilogues.go:87:30:87:35 | zero value for result |
| epilogues.go:87:30:87:35 | implicit read of result | epilogues.go:87:1:98:1 | exit |
| epilogues.go:87:30:87:35 | initialization of result | epilogues.go:88:8:92:2 | function literal |
| epilogues.go:87:30:87:35 | zero value for result | epilogues.go:87:30:87:35 | initialization of result |
| epilogues.go:88:2:92:4 | defer statement | epilogues.go:93:5:93:5 | x |
| epilogues.go:88:8:92:2 | entry | epilogues.go:89:6:89:6 | skip |
| epilogues.go:88:8:92:2 | function literal | epilogues.go:88:2:92:4 | defer statement |
| epilogues.go:88:8:92:4 | function call | epilogues.go:87:1:98:1 | exit |
| epilogues.go:88:8:92:4 | function call | epilogues.go:87:30:87:35 | implicit read of result |
| epilogues.go:89:6:89:6 | assignment to r | epilogues.go:89:22:89:22 | r |
| epilogues.go:89:6:89:6 | skip | epilogues.go:89:11:89:17 | recover |
| epilogues.go:89:11:89:17 | recover | epilogues.go:89:11:89:19 | call to recover |
| epilogues.go:89:11:89:19 | call to recover | epilogues.go:89:6:89:6 | assignment to r |
| epilogues.go:89:22:89:22 | r | epilogues.go:89:27:89:29 | nil |
| epilogues.go:89:22:89:29 | ...!=... | epilogues.go:88:8:92:2 | exit |
| epilogues.go:89:22:89:29 | ...!=... | epilogues.go:89:22:89:29 | ...!=... is false |
| epilogues.go:89:22:89:29 | ...!=... | epilogues.go:89:22:89:29 | ...!=... is true |
| epilogues.go:89:22:89:29 | ...!=... is false | epilogues.go:88:8:92:2 | exit |
| epilogues.go:89:22:89:29 | ...!=... is true | epilogues.go:90:4:90:9 | skip |
| epilogues.go:89:27:89:29 | nil | epilogues.go:89:22:89:29 | ...!=... |
| epilogues.go:90:4:90:9 | assignment to result | epilogues.go:88:8:92:2 | exit |
| epilogues.go:90:4:90:9 | skip | epilogues.go:90:13:90:14 | -... |
| epilogues.go:90:13:90:14 | -... | epilogues.go:90:4:90:9 | assignment to result |
| epilogues.go:93:5:93:5 | x | epilogues.go:93:9:93:9 | 0 |
| epilogues.go:93:5:93:9 | ...<... | epilogues.go:93:5:93:9 | ...<... is false |
| epilogues.go:93:5:93:9 | ...<... | epilogues.go:93:5:93:9 | ...<... is true |
| epilogues.go:93:5:93:9 | ...<... is false | epilogues.go:96:2:96:7 | skip |
| epilogues.go:93:5:93:9 | ...<... is true | epilogues.go:94:3:94:7 | panic |
| epilogues.go:93:9:93:9 | 0 | epilogues.go:93:5:93:9 | ...<... |
| epilogues.go:94:3:94:7 | panic | epilogues.go:94:9:94:13 | "neg" |
| epilogues.go:94:3:94:14 | call to panic | epilogues.go:88:8:92:4 | function call |
| epilogues.go:94:9:94:13 | "neg" | epilogues.go:94:3:94:14 | call to panic |
| epilogues.go:96:2:96:7 | assignment to result | epilogues.go:97:9:97:14 | result |
| epilogues.go:96:2:96:7 | skip | epilogues.go:96:11:96:11 | x |
| epilogues.go:96:11:96:11 | x | epilogues.go:96:15:96:15 | x |
| epilogues.go:96:11:96:15 | ...*... | epilogues.go:96:2:96:7 | assignment to result |
| epilogues.go:96:15:96:15 | x | epilogues.go:96:11:96:15 | ...*... |
| epilogues.go:97:2:97:14 | return statement | epilogues.go:88:8:92:4 | function call |
| epilogues.go:97:9:97:14 | implicit write of result | epilogues.go:97:2:97:14 | return statement |
| epilogues.go:97:9:97:14 | result | epilogues.go:97:9:97:14 | implicit write of result |
| epilogues.go:102:1:110:1 | entry | epilogues.go:102:26:102:26 | argument corresponding to x |
| epilogues.go:102:1:110:1 | function declaration | epilogues.go:115:6:115:22 | skip |
| epilogues.go:102:6:102:24 | skip | epilogues.go:102:1:110:1 | function declaration |
| epilogues.go:102:26:102:26 | argument corresponding to x | epilogues.go:102:26:102:26 | initialization of x |
| epilogues.go:102:26:102:26 | initialization of x | epilogues.go:102:34:102:35 | zero value for ok |
| epilogues.go:102:34:102:35 | implicit read of ok | epilogues.go:102:43:102:43 | implicit read of n |
| epilogues.go:102:34:102:35 | initialization of ok | epilogues.go:102:43:102:43 | zero value for n |
| epilogues.go:102:34:102:35 | zero value for ok | epilogues.go:102:34:102:35 | initialization of ok |
| epilogues.go:102:43:102:43 | implicit read of n | epilogues.go:102:1:110:1 | exit |
| epilogues.go:102:43:102:43 | initialization of n | epilogues.go:103:8:103:17 | epiRecover |
| epilogues.go:102:43:102:43 | zero value for n | epilogues.go:102:43:102:43 | initialization of n |
| epilogues.go:103:2:103:19 | defer statement | epilogues.go:104:5:104:5 | x |
| epilogues.go:103:8:103:17 | epiRecover | epilogues.go:103:2:103:19 | defer statement |
| epilogues.go:103:8:103:19 | call to epiRecover | epilogues.go:102:1:110:1 | exit |
| epilogues.go:103:8:103:19 | call to epiRecover | epilogues.go:102:34:102:35 | implicit read of ok |
| epilogues.go:104:5:104:5 | x | epilogues.go:104:10:104:10 | 0 |
| epilogues.go:104:5:104:10 | ...==... | epilogues.go:104:5:104:10 | ...==... is false |
| epilogues.go:104:5:104:10 | ...==... | epilogues.go:104:5:104:10 | ...==... is true |
| epilogues.go:104:5:104:10 | ...==... is false | epilogues.go:107:2:107:2 | skip |
| epilogues.go:104:5:104:10 | ...==... is true | epilogues.go:105:3:105:8 | return statement |
| epilogues.go:104:10:104:10 | 0 | epilogues.go:104:5:104:10 | ...==... |
| epilogues.go:105:3:105:8 | return statement | epilogues.go:103:8:103:19 | call to epiRecover |
| epilogues.go:107:2:107:2 | assignment to n | epilogues.go:108:2:108:3 | skip |
| epilogues.go:107:2:107:2 | skip | epilogues.go:107:6:107:6 | x |
| epilogues.go:107:6:107:6 | x | epilogues.go:107:2:107:2 | assignment to n |
| epilogues.go:108:2:108:3 | assignment to ok | epilogues.go:109:2:109:7 | return statement |
| epilogues.go:108:2:108:3 | skip | epilogues.go:108:7:108:10 | true |
| epilogues.go:108:7:108:10 | true | epilogues.go:108:2:108:3 | assignment to ok |
| epilogues.go:109:2:109:7 | return statement | epilogues.go:103:8:103:19 | call to epiRecover |
| epilogues.go:115:1:118:1 | entry | epilogues.go:116:8:116:17 | epiRecover |
| epilogues.go:115:1:118:1 | function declaration | epilogues.go:0:0:0:0 | exit |
| epilogues.go:115:6:115:22 | skip | epilogues.go:115:1:118:1 | function declaration |
| epilogues.go:116:2:116:19 | defer statement | epilogues.go:117:2:117:6 | panic |
| epilogues.go:116:8:116:17 | epiRecover | epilogues.go:116:2:116:19 | defer statement |
| epilogues.go:116:8:116:19 | call to epiRecover | epilogues.go:115:1:118:1 | exit |
| epilogues.go:117:2:117:6 | panic | epilogues.go:117:8:117:13 | "boom" |
| epilogues.go:117:2:117:14 | call to panic | epilogues.go:116:8:116:19 | call to epiRecover |
| epilogues.go:117:8:117:13 | "boom" | epilogues.go:117:2:117:14 | call to panic |
| equalitytests.go:0:0:0:0 | entry | equalitytests.go:3:1:5:1 | skip | | equalitytests.go:0:0:0:0 | entry | equalitytests.go:3:1:5:1 | skip |
| equalitytests.go:3:1:5:1 | skip | equalitytests.go:7:1:9:1 | skip | | equalitytests.go:3:1:5:1 | skip | equalitytests.go:7:1:9:1 | skip |
| equalitytests.go:7:1:9:1 | skip | equalitytests.go:11:6:11:18 | skip | | equalitytests.go:7:1:9:1 | skip | equalitytests.go:11:6:11:18 | skip |

View File

@@ -1,3 +1,4 @@
| epilogues.go:115:6:115:22 | epiRecoverUnnamed | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.epiRecoverUnnamed |
| file://:0:0:0:0 | Exit | os.Exit | | file://:0:0:0:0 | Exit | os.Exit |
| file://:0:0:0:0 | Fatal | log.Fatal | | file://:0:0:0:0 | Fatal | log.Fatal |
| file://:0:0:0:0 | Fatal | log.Logger.Fatal | | file://:0:0:0:0 | Fatal | log.Logger.Fatal |

View File

@@ -0,0 +1,118 @@
package main
import "fmt"
// epiLogger has methods with both pointer and value receivers, used to check
// that the receiver and arguments of a deferred call are evaluated at the
// `defer` statement rather than in the function epilogue.
type epiLogger struct {
prefix string
}
func (l *epiLogger) log(msg string, code int) {
fmt.Println(l.prefix, msg, code)
}
func (l epiLogger) logValue(msg string) {
fmt.Println(l.prefix, msg)
}
// epiRecover recovers from a panic. It is used as a deferred function so we can
// check that control flow returns to the result-read nodes and the normal exit
// node after recovering.
func epiRecover() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}
// epiPlain has no named result variable and a single `return` with a child
// expression.
func epiPlain(x int) int {
return x * 2
}
// epiVoid has no named result variable and no `return` statement at all.
func epiVoid() {
fmt.Println("void")
}
// epiNamedMixed has named result variables and a mix of a bare `return` (no
// child expressions) and a `return` with child expressions.
func epiNamedMixed(x int) (result int, err error) {
if x < 0 {
result = -x
return
}
return x, nil
}
// epiNamedBareOnly has a named result variable and only a bare `return`.
func epiNamedBareOnly(x int) (n int) {
n = x + 1
return
}
// epiDeferReceiverArgs has a deferred call with a (pointer) receiver and
// arguments that are expressions, so we can check the receiver `l` and the
// arguments `"count"` and `len(items)` are evaluated at the `defer` statement.
func epiDeferReceiverArgs(l *epiLogger, items []int) {
defer l.log("count", len(items))
fmt.Println("processing", len(items))
}
// epiDeferValueReceiver has deferred calls with a value receiver and an
// address-of receiver, both with arguments evaluated at the `defer` statement.
func epiDeferValueReceiver(prefix string) {
l := epiLogger{prefix: prefix}
defer l.logValue("bye")
defer (&l).log("ptr", 7)
fmt.Println("body")
}
// epiDeferFuncLit has a deferred function literal with parameters, so we can
// check that the arguments `"done"` and `x+1` are evaluated at the `defer`
// statement and that control flow enters the function literal body when it is
// invoked at the function epilogue.
func epiDeferFuncLit(x int) {
defer func(label string, n int) {
fmt.Println(label, n)
}("done", x+1)
fmt.Println("body", x)
}
// epiRecoverNamed has a named result variable and a deferred closure containing
// `recover()`. After recovering on the panic path, control flow should return
// to the result-read nodes and the normal exit node.
func epiRecoverNamed(x int) (result int) {
defer func() {
if r := recover(); r != nil {
result = -1
}
}()
if x < 0 {
panic("neg")
}
result = x * x
return result
}
// epiRecoverNamedBare has named result variables, a deferred function
// containing `recover()`, and only bare `return` statements.
func epiRecoverNamedBare(x int) (ok bool, n int) {
defer epiRecover()
if x == 0 {
return
}
n = x
ok = true
return
}
// epiRecoverUnnamed has no named result variables and a deferred function
// containing `recover()`; after recovering, control flow should reach the
// normal exit node directly (there are no result-read nodes).
func epiRecoverUnnamed() {
defer epiRecover()
panic("boom")
}

View File

@@ -213,9 +213,11 @@ class ExprWithPointsTo extends Expr {
* Gets what this expression might "refer-to" in the given `context`. * Gets what this expression might "refer-to" in the given `context`.
*/ */
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) { predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
this.getAFlowNode() exists(ControlFlowNode this_, ControlFlowNode origin_ |
.(ControlFlowNodeWithPointsTo) this_.getNode() = this and origin_.getNode() = origin
.refersTo(context, obj, cls, origin.getAFlowNode()) |
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
)
} }
/** /**
@@ -226,7 +228,11 @@ class ExprWithPointsTo extends Expr {
*/ */
pragma[nomagic] pragma[nomagic]
predicate refersTo(Object obj, AstNode origin) { predicate refersTo(Object obj, AstNode origin) {
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode()) exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
)
} }
/** /**
@@ -240,16 +246,22 @@ class ExprWithPointsTo extends Expr {
* in the given `context`. * in the given `context`.
*/ */
predicate pointsTo(Context context, Value value, AstNode origin) { predicate pointsTo(Context context, Value value, AstNode origin) {
this.getAFlowNode() exists(ControlFlowNode this_, ControlFlowNode origin_ |
.(ControlFlowNodeWithPointsTo) this_.getNode() = this and origin_.getNode() = origin
.pointsTo(context, value, origin.getAFlowNode()) |
this_.(ControlFlowNodeWithPointsTo).pointsTo(context, value, origin_)
)
} }
/** /**
* Holds if this expression might "point-to" to `value` which is from `origin`. * Holds if this expression might "point-to" to `value` which is from `origin`.
*/ */
predicate pointsTo(Value value, AstNode origin) { predicate pointsTo(Value value, AstNode origin) {
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode()) exists(ControlFlowNode this_, ControlFlowNode origin_ |
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
)
} }
/** /**
@@ -475,7 +487,10 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics {
not non_coupling_method(result) and not non_coupling_method(result) and
exists(Call call | call.getScope() = this | exists(Call call | call.getScope() = this |
exists(FunctionObject callee | callee.getFunction() = result | exists(FunctionObject callee | callee.getFunction() = result |
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee) exists(CallNode call_ |
call_.getNode() = call and
call_.getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
)
) )
or or
exists(Attribute a | call.getFunc() = a | exists(Attribute a | call.getFunc() = a |

View File

@@ -64,7 +64,7 @@ private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
private predicate preferred_jump_to_defn(Expr use, Definition def) { private predicate preferred_jump_to_defn(Expr use, Definition def) {
not use instanceof ClassExpr and not use instanceof ClassExpr and
not use instanceof FunctionExpr and not use instanceof FunctionExpr and
jump_to_defn(use.getAFlowNode(), def) exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def))
} }
private predicate unique_jump_to_defn(Expr use, Definition def) { private predicate unique_jump_to_defn(Expr use, Definition def) {
@@ -452,7 +452,7 @@ private predicate self_parameter_jump_to_defn_attribute(
* This exists primarily for testing use `getPreferredDefinition()` instead. * This exists primarily for testing use `getPreferredDefinition()` instead.
*/ */
Definition getADefinition(Expr use) { Definition getADefinition(Expr use) {
jump_to_defn(use.getAFlowNode(), result) and exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and
not use instanceof Call and not use instanceof Call and
not use.isArtificial() and not use.isArtificial() and
// Not the use itself // Not the use itself

View File

@@ -0,0 +1,5 @@
---
category: deprecated
---
* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.

View File

@@ -17,12 +17,17 @@ abstract class AstNode extends AstNode_ {
abstract Scope getScope(); abstract Scope getScope();
/** /**
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
* being removed to untangle the AST and CFG hierarchies in preparation for
* migrating the dataflow library off the legacy CFG.
*
* Gets a flow node corresponding directly to this node. * Gets a flow node corresponding directly to this node.
* NOTE: For some statements and other purely syntactic elements, * NOTE: For some statements and other purely syntactic elements,
* there may not be a `ControlFlowNode` * there may not be a `ControlFlowNode`.
*/ */
cached cached
ControlFlowNode getAFlowNode() { deprecated ControlFlowNode getAFlowNode() {
Stages::AST::ref() and Stages::AST::ref() and
py_flow_bb_node(result, this, _, _) py_flow_bb_node(result, this, _, _)
} }

View File

@@ -28,7 +28,9 @@ class Expr extends Expr_, AstNode {
/** Whether this expression may have a side effect (as determined purely from its syntax) */ /** Whether this expression may have a side effect (as determined purely from its syntax) */
predicate hasSideEffects() { predicate hasSideEffects() {
/* If an exception raised by this expression handled, count that as a side effect */ /* If an exception raised by this expression handled, count that as a side effect */
this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt exists(ControlFlowNode n | n.getNode() = this |
n.getASuccessor().getNode() instanceof ExceptStmt
)
or or
this.getASubExpression().hasSideEffects() this.getASubExpression().hasSideEffects()
} }
@@ -68,7 +70,7 @@ class Attribute extends Attribute_ {
/* syntax: Expr.name */ /* syntax: Expr.name */
override Expr getASubExpression() { result = this.getObject() } override Expr getASubExpression() { result = this.getObject() }
override AttrNode getAFlowNode() { result = super.getAFlowNode() } deprecated override AttrNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets the name of this attribute. That is the `name` in `obj.name` */ /** Gets the name of this attribute. That is the `name` in `obj.name` */
string getName() { result = Attribute_.super.getAttr() } string getName() { result = Attribute_.super.getAttr() }
@@ -97,7 +99,7 @@ class Subscript extends Subscript_ {
Expr getObject() { result = Subscript_.super.getValue() } Expr getObject() { result = Subscript_.super.getValue() }
override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } deprecated override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** A call expression, such as `func(...)` */ /** A call expression, such as `func(...)` */
@@ -113,7 +115,7 @@ class Call extends Call_ {
override string toString() { result = this.getFunc().toString() + "()" } override string toString() { result = this.getFunc().toString() + "()" }
override CallNode getAFlowNode() { result = super.getAFlowNode() } deprecated override CallNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets a tuple (*) argument of this call. */ /** Gets a tuple (*) argument of this call. */
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
@@ -201,7 +203,7 @@ class IfExp extends IfExp_ {
result = this.getTest() or result = this.getBody() or result = this.getOrelse() result = this.getTest() or result = this.getBody() or result = this.getOrelse()
} }
override IfExprNode getAFlowNode() { result = super.getAFlowNode() } deprecated override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */ /** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
@@ -411,7 +413,7 @@ class PlaceHolder extends PlaceHolder_ {
override string toString() { result = "$" + this.getId() } override string toString() { result = "$" + this.getId() }
override NameNode getAFlowNode() { result = super.getAFlowNode() } deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */ /** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
@@ -478,7 +480,7 @@ class Name extends Name_ {
override string toString() { result = this.getId() } override string toString() { result = this.getId() }
override NameNode getAFlowNode() { result = super.getAFlowNode() } deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
override predicate isArtificial() { override predicate isArtificial() {
/* Artificial variable names in comprehensions all start with "." */ /* Artificial variable names in comprehensions all start with "." */
@@ -585,7 +587,7 @@ abstract class NameConstant extends Name, ImmutableLiteral {
override predicate isConstant() { any() } override predicate isConstant() { any() }
override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } deprecated override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
override predicate isArtificial() { none() } override predicate isArtificial() { none() }
} }

View File

@@ -1,7 +1,7 @@
overlay[local] overlay[local]
module; module;
import python import python as Py
private import semmle.python.internal.CachedStages private import semmle.python.internal.CachedStages
private import codeql.controlflow.BasicBlock as BB private import codeql.controlflow.BasicBlock as BB
@@ -17,7 +17,7 @@ private import codeql.controlflow.BasicBlock as BB
*/ */
private predicate augstore(ControlFlowNode load, ControlFlowNode store) { private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
exists(Expr load_store | exists(AugAssign aa | aa.getTarget() = load_store) | exists(Py::Expr load_store | exists(Py::AugAssign aa | aa.getTarget() = load_store) |
toAst(load) = load_store and toAst(load) = load_store and
toAst(store) = load_store and toAst(store) = load_store and
load.strictlyDominates(store) load.strictlyDominates(store)
@@ -25,7 +25,7 @@ private predicate augstore(ControlFlowNode load, ControlFlowNode store) {
} }
/** A non-dispatched getNode() to avoid negative recursion issues */ /** A non-dispatched getNode() to avoid negative recursion issues */
private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) } private Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
/** /**
* A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes, * A control flow node. Control flow nodes have a many-to-one relation with syntactic nodes,
@@ -35,19 +35,19 @@ private AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) }
class ControlFlowNode extends @py_flow_node { class ControlFlowNode extends @py_flow_node {
/** Whether this control flow node is a load (including those in augmented assignments) */ /** Whether this control flow node is a load (including those in augmented assignments) */
predicate isLoad() { predicate isLoad() {
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
} }
/** Whether this control flow node is a store (including those in augmented assignments) */ /** Whether this control flow node is a store (including those in augmented assignments) */
predicate isStore() { predicate isStore() {
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this))
} }
/** Whether this control flow node is a delete */ /** Whether this control flow node is a delete */
predicate isDelete() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } predicate isDelete() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) }
/** Whether this control flow node is a parameter */ /** Whether this control flow node is a parameter */
predicate isParameter() { exists(Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } predicate isParameter() { exists(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) }
/** Whether this control flow node is a store in an augmented assignment */ /** Whether this control flow node is a store in an augmented assignment */
predicate isAugStore() { augstore(_, this) } predicate isAugStore() { augstore(_, this) }
@@ -57,61 +57,61 @@ class ControlFlowNode extends @py_flow_node {
/** Whether this flow node corresponds to a literal */ /** Whether this flow node corresponds to a literal */
predicate isLiteral() { predicate isLiteral() {
toAst(this) instanceof Bytes toAst(this) instanceof Py::Bytes
or or
toAst(this) instanceof Dict toAst(this) instanceof Py::Dict
or or
toAst(this) instanceof DictComp toAst(this) instanceof Py::DictComp
or or
toAst(this) instanceof Set toAst(this) instanceof Py::Set
or or
toAst(this) instanceof SetComp toAst(this) instanceof Py::SetComp
or or
toAst(this) instanceof Ellipsis toAst(this) instanceof Py::Ellipsis
or or
toAst(this) instanceof GeneratorExp toAst(this) instanceof Py::GeneratorExp
or or
toAst(this) instanceof Lambda toAst(this) instanceof Py::Lambda
or or
toAst(this) instanceof ListComp toAst(this) instanceof Py::ListComp
or or
toAst(this) instanceof List toAst(this) instanceof Py::List
or or
toAst(this) instanceof Num toAst(this) instanceof Py::Num
or or
toAst(this) instanceof Tuple toAst(this) instanceof Py::Tuple
or or
toAst(this) instanceof Unicode toAst(this) instanceof Py::Unicode
or or
toAst(this) instanceof NameConstant toAst(this) instanceof Py::NameConstant
} }
/** Whether this flow node corresponds to an attribute expression */ /** Whether this flow node corresponds to an attribute expression */
predicate isAttribute() { toAst(this) instanceof Attribute } predicate isAttribute() { toAst(this) instanceof Py::Attribute }
/** Whether this flow node corresponds to an subscript expression */ /** Whether this flow node corresponds to an subscript expression */
predicate isSubscript() { toAst(this) instanceof Subscript } predicate isSubscript() { toAst(this) instanceof Py::Subscript }
/** Whether this flow node corresponds to an import member */ /** Whether this flow node corresponds to an import member */
predicate isImportMember() { toAst(this) instanceof ImportMember } predicate isImportMember() { toAst(this) instanceof Py::ImportMember }
/** Whether this flow node corresponds to a call */ /** Whether this flow node corresponds to a call */
predicate isCall() { toAst(this) instanceof Call } predicate isCall() { toAst(this) instanceof Py::Call }
/** Whether this flow node is the first in a module */ /** Whether this flow node is the first in a module */
predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module } predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Py::Module }
/** Whether this flow node corresponds to an import */ /** Whether this flow node corresponds to an import */
predicate isImport() { toAst(this) instanceof ImportExpr } predicate isImport() { toAst(this) instanceof Py::ImportExpr }
/** Whether this flow node corresponds to a conditional expression */ /** Whether this flow node corresponds to a conditional expression */
predicate isIfExp() { toAst(this) instanceof IfExp } predicate isIfExp() { toAst(this) instanceof Py::IfExp }
/** Whether this flow node corresponds to a function definition expression */ /** Whether this flow node corresponds to a function definition expression */
predicate isFunction() { toAst(this) instanceof FunctionExpr } predicate isFunction() { toAst(this) instanceof Py::FunctionExpr }
/** Whether this flow node corresponds to a class definition expression */ /** Whether this flow node corresponds to a class definition expression */
predicate isClass() { toAst(this) instanceof ClassExpr } predicate isClass() { toAst(this) instanceof Py::ClassExpr }
/** Gets a predecessor of this flow node */ /** Gets a predecessor of this flow node */
ControlFlowNode getAPredecessor() { this = result.getASuccessor() } ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
@@ -123,25 +123,25 @@ class ControlFlowNode extends @py_flow_node {
ControlFlowNode getImmediateDominator() { py_idoms(this, result) } ControlFlowNode getImmediateDominator() { py_idoms(this, result) }
/** Gets the syntactic element corresponding to this flow node */ /** Gets the syntactic element corresponding to this flow node */
AstNode getNode() { py_flow_bb_node(this, result, _, _) } Py::AstNode getNode() { py_flow_bb_node(this, result, _, _) }
/** Gets a textual representation of this element. */ /** Gets a textual representation of this element. */
cached cached
string toString() { string toString() {
Stages::AST::ref() and Stages::AST::ref() and
// Since modules can have ambigous names, entry nodes can too, if we do not collate them. // Since modules can have ambigous names, entry nodes can too, if we do not collate them.
exists(Scope s | s.getEntryNode() = this | exists(Py::Scope s | s.getEntryNode() = this |
result = "Entry node for " + concat( | | s.toString(), ",") result = "Entry node for " + concat( | | s.toString(), ",")
) )
or or
exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) exists(Py::Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
or or
not exists(Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and not exists(Py::Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and
result = "ControlFlowNode for " + this.getNode().toString() result = "ControlFlowNode for " + this.getNode().toString()
} }
/** Gets the location of this ControlFlowNode */ /** Gets the location of this ControlFlowNode */
Location getLocation() { result = this.getNode().getLocation() } Py::Location getLocation() { result = this.getNode().getLocation() }
/** Whether this flow node is the first in its scope */ /** Whether this flow node is the first in its scope */
predicate isEntryNode() { py_scope_flow(this, _, -1) } predicate isEntryNode() { py_scope_flow(this, _, -1) }
@@ -151,9 +151,9 @@ class ControlFlowNode extends @py_flow_node {
/** Gets the scope containing this flow node */ /** Gets the scope containing this flow node */
cached cached
Scope getScope() { Py::Scope getScope() {
Stages::AST::ref() and Stages::AST::ref() and
if this.getNode() instanceof Scope if this.getNode() instanceof Py::Scope
then then
/* Entry or exit node */ /* Entry or exit node */
result = this.getNode() result = this.getNode()
@@ -161,7 +161,7 @@ class ControlFlowNode extends @py_flow_node {
} }
/** Gets the enclosing module */ /** Gets the enclosing module */
Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } Py::Module getEnclosingModule() { result = this.getScope().getEnclosingModule() }
/** Gets a successor for this node if the relevant condition is True. */ /** Gets a successor for this node if the relevant condition is True. */
ControlFlowNode getATrueSuccessor() { ControlFlowNode getATrueSuccessor() {
@@ -188,7 +188,7 @@ class ControlFlowNode extends @py_flow_node {
} }
/** Whether the scope may be exited as a result of this node raising an exception */ /** Whether the scope may be exited as a result of this node raising an exception */
predicate isExceptionalExit(Scope s) { py_scope_flow(this, s, 1) } predicate isExceptionalExit(Py::Scope s) { py_scope_flow(this, s, 1) }
/** Whether this node is a normal (non-exceptional) exit */ /** Whether this node is a normal (non-exceptional) exit */
predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) } predicate isNormalExit() { py_scope_flow(this, _, 0) or py_scope_flow(this, _, 2) }
@@ -236,7 +236,7 @@ class ControlFlowNode extends @py_flow_node {
/* join-ordering helper for `getAChild() */ /* join-ordering helper for `getAChild() */
pragma[noinline] pragma[noinline]
private ControlFlowNode getExprChild(BasicBlock dom) { private ControlFlowNode getExprChild(BasicBlock dom) {
this.getNode().(Expr).getAChildNode() = result.getNode() and this.getNode().(Py::Expr).getAChildNode() = result.getNode() and
result.getBasicBlock().dominates(dom) and result.getBasicBlock().dominates(dom) and
not this instanceof UnaryExprNode not this instanceof UnaryExprNode
} }
@@ -249,16 +249,16 @@ class ControlFlowNode extends @py_flow_node {
*/ */
private class AnyNode extends ControlFlowNode { private class AnyNode extends ControlFlowNode {
override AstNode getNode() { result = super.getNode() } override Py::AstNode getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a call expression, such as `func(...)` */ /** A control flow node corresponding to a call expression, such as `func(...)` */
class CallNode extends ControlFlowNode { class CallNode extends ControlFlowNode {
CallNode() { toAst(this) instanceof Call } CallNode() { toAst(this) instanceof Py::Call }
/** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */ /** Gets the flow node corresponding to the function expression for the call corresponding to this flow node */
ControlFlowNode getFunction() { ControlFlowNode getFunction() {
exists(Call c | exists(Py::Call c |
this.getNode() = c and this.getNode() = c and
c.getFunc() = result.getNode() and c.getFunc() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -267,7 +267,7 @@ class CallNode extends ControlFlowNode {
/** Gets the flow node corresponding to the n'th positional argument of the call corresponding to this flow node */ /** Gets the flow node corresponding to the n'th positional argument of the call corresponding to this flow node */
ControlFlowNode getArg(int n) { ControlFlowNode getArg(int n) {
exists(Call c | exists(Py::Call c |
this.getNode() = c and this.getNode() = c and
c.getArg(n) = result.getNode() and c.getArg(n) = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -276,7 +276,7 @@ class CallNode extends ControlFlowNode {
/** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */ /** Gets the flow node corresponding to the named argument of the call corresponding to this flow node */
ControlFlowNode getArgByName(string name) { ControlFlowNode getArgByName(string name) {
exists(Call c, Keyword k | exists(Py::Call c, Py::Keyword k |
this.getNode() = c and this.getNode() = c and
k = c.getANamedArg() and k = c.getANamedArg() and
k.getValue() = result.getNode() and k.getValue() = result.getNode() and
@@ -292,7 +292,7 @@ class CallNode extends ControlFlowNode {
result = this.getArgByName(_) result = this.getArgByName(_)
} }
override Call getNode() { result = super.getNode() } override Py::Call getNode() { result = super.getNode() }
predicate isDecoratorCall() { predicate isDecoratorCall() {
this.isClassDecoratorCall() this.isClassDecoratorCall()
@@ -301,11 +301,11 @@ class CallNode extends ControlFlowNode {
} }
predicate isClassDecoratorCall() { predicate isClassDecoratorCall() {
exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall()) exists(Py::ClassExpr cls | this.getNode() = cls.getADecoratorCall())
} }
predicate isFunctionDecoratorCall() { predicate isFunctionDecoratorCall() {
exists(FunctionExpr func | this.getNode() = func.getADecoratorCall()) exists(Py::FunctionExpr func | this.getNode() = func.getADecoratorCall())
} }
/** Gets the first tuple (*) argument of this call, if any. */ /** Gets the first tuple (*) argument of this call, if any. */
@@ -323,11 +323,11 @@ class CallNode extends ControlFlowNode {
/** A control flow corresponding to an attribute expression, such as `value.attr` */ /** A control flow corresponding to an attribute expression, such as `value.attr` */
class AttrNode extends ControlFlowNode { class AttrNode extends ControlFlowNode {
AttrNode() { toAst(this) instanceof Attribute } AttrNode() { toAst(this) instanceof Py::Attribute }
/** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */ /** Gets the flow node corresponding to the object of the attribute expression corresponding to this flow node */
ControlFlowNode getObject() { ControlFlowNode getObject() {
exists(Attribute a | exists(Py::Attribute a |
this.getNode() = a and this.getNode() = a and
a.getObject() = result.getNode() and a.getObject() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -339,7 +339,7 @@ class AttrNode extends ControlFlowNode {
* with the matching name * with the matching name
*/ */
ControlFlowNode getObject(string name) { ControlFlowNode getObject(string name) {
exists(Attribute a | exists(Py::Attribute a |
this.getNode() = a and this.getNode() = a and
a.getObject() = result.getNode() and a.getObject() = result.getNode() and
a.getName() = name and a.getName() = name and
@@ -348,57 +348,57 @@ class AttrNode extends ControlFlowNode {
} }
/** Gets the attribute name of the attribute expression corresponding to this flow node */ /** Gets the attribute name of the attribute expression corresponding to this flow node */
string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) } string getName() { exists(Py::Attribute a | this.getNode() = a and a.getName() = result) }
override Attribute getNode() { result = super.getNode() } override Py::Attribute getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a `from ... import ...` expression */ /** A control flow node corresponding to a `from ... import ...` expression */
class ImportMemberNode extends ControlFlowNode { class ImportMemberNode extends ControlFlowNode {
ImportMemberNode() { toAst(this) instanceof ImportMember } ImportMemberNode() { toAst(this) instanceof Py::ImportMember }
/** /**
* Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node, * Gets the flow node corresponding to the module in the import-member expression corresponding to this flow node,
* with the matching name * with the matching name
*/ */
ControlFlowNode getModule(string name) { ControlFlowNode getModule(string name) {
exists(ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | exists(Py::ImportMember i | this.getNode() = i and i.getModule() = result.getNode() |
i.getName() = name and i.getName() = name and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
) )
} }
override ImportMember getNode() { result = super.getNode() } override Py::ImportMember getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to an artificial expression representing an import */ /** A control flow node corresponding to an artificial expression representing an import */
class ImportExprNode extends ControlFlowNode { class ImportExprNode extends ControlFlowNode {
ImportExprNode() { toAst(this) instanceof ImportExpr } ImportExprNode() { toAst(this) instanceof Py::ImportExpr }
override ImportExpr getNode() { result = super.getNode() } override Py::ImportExpr getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a `from ... import *` statement */ /** A control flow node corresponding to a `from ... import *` statement */
class ImportStarNode extends ControlFlowNode { class ImportStarNode extends ControlFlowNode {
ImportStarNode() { toAst(this) instanceof ImportStar } ImportStarNode() { toAst(this) instanceof Py::ImportStar }
/** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */ /** Gets the flow node corresponding to the module in the import-star corresponding to this flow node */
ControlFlowNode getModule() { ControlFlowNode getModule() {
exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | exists(Py::ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
) )
} }
override ImportStar getNode() { result = super.getNode() } override Py::ImportStar getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a subscript expression, such as `value[slice]` */ /** A control flow node corresponding to a subscript expression, such as `value[slice]` */
class SubscriptNode extends ControlFlowNode { class SubscriptNode extends ControlFlowNode {
SubscriptNode() { toAst(this) instanceof Subscript } SubscriptNode() { toAst(this) instanceof Py::Subscript }
/** flow node corresponding to the value of the sequence in a subscript operation */ /** flow node corresponding to the value of the sequence in a subscript operation */
ControlFlowNode getObject() { ControlFlowNode getObject() {
exists(Subscript s | exists(Py::Subscript s |
this.getNode() = s and this.getNode() = s and
s.getObject() = result.getNode() and s.getObject() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -407,23 +407,23 @@ class SubscriptNode extends ControlFlowNode {
/** flow node corresponding to the index in a subscript operation */ /** flow node corresponding to the index in a subscript operation */
ControlFlowNode getIndex() { ControlFlowNode getIndex() {
exists(Subscript s | exists(Py::Subscript s |
this.getNode() = s and this.getNode() = s and
s.getIndex() = result.getNode() and s.getIndex() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
) )
} }
override Subscript getNode() { result = super.getNode() } override Py::Subscript getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a comparison operation, such as `x<y` */ /** A control flow node corresponding to a comparison operation, such as `x<y` */
class CompareNode extends ControlFlowNode { class CompareNode extends ControlFlowNode {
CompareNode() { toAst(this) instanceof Compare } CompareNode() { toAst(this) instanceof Py::Compare }
/** Whether left and right are a pair of operands for this comparison */ /** Whether left and right are a pair of operands for this comparison */
predicate operands(ControlFlowNode left, Cmpop op, ControlFlowNode right) { predicate operands(ControlFlowNode left, Py::Cmpop op, ControlFlowNode right) {
exists(Compare c, Expr eleft, Expr eright | exists(Py::Compare c, Py::Expr eleft, Py::Expr eright |
this.getNode() = c and left.getNode() = eleft and right.getNode() = eright this.getNode() = c and left.getNode() = eleft and right.getNode() = eright
| |
eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0) eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0)
@@ -436,26 +436,26 @@ class CompareNode extends ControlFlowNode {
right.getBasicBlock().dominates(this.getBasicBlock()) right.getBasicBlock().dominates(this.getBasicBlock())
} }
override Compare getNode() { result = super.getNode() } override Py::Compare getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a conditional expression such as, `body if test else orelse` */ /** A control flow node corresponding to a conditional expression such as, `body if test else orelse` */
class IfExprNode extends ControlFlowNode { class IfExprNode extends ControlFlowNode {
IfExprNode() { toAst(this) instanceof IfExp } IfExprNode() { toAst(this) instanceof Py::IfExp }
/** flow node corresponding to one of the operands of an if-expression */ /** flow node corresponding to one of the operands of an if-expression */
ControlFlowNode getAnOperand() { result = this.getAPredecessor() } ControlFlowNode getAnOperand() { result = this.getAPredecessor() }
override IfExp getNode() { result = super.getNode() } override Py::IfExp getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */ /** A control flow node corresponding to an assignment expression such as `lhs := rhs`. */
class AssignmentExprNode extends ControlFlowNode { class AssignmentExprNode extends ControlFlowNode {
AssignmentExprNode() { toAst(this) instanceof AssignExpr } AssignmentExprNode() { toAst(this) instanceof Py::AssignExpr }
/** Gets the flow node corresponding to the left-hand side of the assignment expression */ /** Gets the flow node corresponding to the left-hand side of the assignment expression */
ControlFlowNode getTarget() { ControlFlowNode getTarget() {
exists(AssignExpr a | exists(Py::AssignExpr a |
this.getNode() = a and this.getNode() = a and
a.getTarget() = result.getNode() and a.getTarget() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -464,27 +464,27 @@ class AssignmentExprNode extends ControlFlowNode {
/** Gets the flow node corresponding to the right-hand side of the assignment expression */ /** Gets the flow node corresponding to the right-hand side of the assignment expression */
ControlFlowNode getValue() { ControlFlowNode getValue() {
exists(AssignExpr a | exists(Py::AssignExpr a |
this.getNode() = a and this.getNode() = a and
a.getValue() = result.getNode() and a.getValue() = result.getNode() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
) )
} }
override AssignExpr getNode() { result = super.getNode() } override Py::AssignExpr getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a binary expression, such as `x + y` */ /** A control flow node corresponding to a binary expression, such as `x + y` */
class BinaryExprNode extends ControlFlowNode { class BinaryExprNode extends ControlFlowNode {
BinaryExprNode() { toAst(this) instanceof BinaryExpr } BinaryExprNode() { toAst(this) instanceof Py::BinaryExpr }
/** flow node corresponding to one of the operands of a binary expression */ /** flow node corresponding to one of the operands of a binary expression */
ControlFlowNode getAnOperand() { result = this.getLeft() or result = this.getRight() } ControlFlowNode getAnOperand() { result = this.getLeft() or result = this.getRight() }
override BinaryExpr getNode() { result = super.getNode() } override Py::BinaryExpr getNode() { result = super.getNode() }
ControlFlowNode getLeft() { ControlFlowNode getLeft() {
exists(BinaryExpr b | exists(Py::BinaryExpr b |
this.getNode() = b and this.getNode() = b and
result.getNode() = b.getLeft() and result.getNode() = b.getLeft() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -492,7 +492,7 @@ class BinaryExprNode extends ControlFlowNode {
} }
ControlFlowNode getRight() { ControlFlowNode getRight() {
exists(BinaryExpr b | exists(Py::BinaryExpr b |
this.getNode() = b and this.getNode() = b and
result.getNode() = b.getRight() and result.getNode() = b.getRight() and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -500,11 +500,11 @@ class BinaryExprNode extends ControlFlowNode {
} }
/** Gets the operator of this binary expression node. */ /** Gets the operator of this binary expression node. */
Operator getOp() { result = this.getNode().getOp() } Py::Operator getOp() { result = this.getNode().getOp() }
/** Whether left and right are a pair of operands for this binary expression */ /** Whether left and right are a pair of operands for this binary expression */
predicate operands(ControlFlowNode left, Operator op, ControlFlowNode right) { predicate operands(ControlFlowNode left, Py::Operator op, ControlFlowNode right) {
exists(BinaryExpr b, Expr eleft, Expr eright | exists(Py::BinaryExpr b, Py::Expr eleft, Py::Expr eright |
this.getNode() = b and left.getNode() = eleft and right.getNode() = eright this.getNode() = b and left.getNode() = eleft and right.getNode() = eright
| |
eleft = b.getLeft() and eright = b.getRight() and op = b.getOp() eleft = b.getLeft() and eright = b.getRight() and op = b.getOp()
@@ -516,20 +516,20 @@ class BinaryExprNode extends ControlFlowNode {
/** A control flow node corresponding to a boolean shortcut (and/or) operation */ /** A control flow node corresponding to a boolean shortcut (and/or) operation */
class BoolExprNode extends ControlFlowNode { class BoolExprNode extends ControlFlowNode {
BoolExprNode() { toAst(this) instanceof BoolExpr } BoolExprNode() { toAst(this) instanceof Py::BoolExpr }
/** flow node corresponding to one of the operands of a boolean expression */ /** flow node corresponding to one of the operands of a boolean expression */
ControlFlowNode getAnOperand() { ControlFlowNode getAnOperand() {
exists(BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and exists(Py::BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
this.getBasicBlock().dominates(result.getBasicBlock()) this.getBasicBlock().dominates(result.getBasicBlock())
} }
override BoolExpr getNode() { result = super.getNode() } override Py::BoolExpr getNode() { result = super.getNode() }
} }
/** A control flow node corresponding to a unary expression: (`+x`), (`-x`) or (`~x`) */ /** A control flow node corresponding to a unary expression: (`+x`), (`-x`) or (`~x`) */
class UnaryExprNode extends ControlFlowNode { class UnaryExprNode extends ControlFlowNode {
UnaryExprNode() { toAst(this) instanceof UnaryExpr } UnaryExprNode() { toAst(this) instanceof Py::UnaryExpr }
/** /**
* Gets flow node corresponding to the operand of a unary expression. * Gets flow node corresponding to the operand of a unary expression.
@@ -540,7 +540,7 @@ class UnaryExprNode extends ControlFlowNode {
*/ */
ControlFlowNode getOperand() { result = this.getAPredecessor() } ControlFlowNode getOperand() { result = this.getAPredecessor() }
override UnaryExpr getNode() { result = super.getNode() } override Py::UnaryExpr getNode() { result = super.getNode() }
override ControlFlowNode getAChild() { result = this.getAPredecessor() } override ControlFlowNode getAChild() { result = this.getAPredecessor() }
} }
@@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
cached cached
DefinitionNode() { DefinitionNode() {
Stages::AST::ref() and Stages::AST::ref() and
exists(Assign a | a.getATarget().getAFlowNode() = this) exists(Py::Assign a | this.getNode() = a.getATarget())
or or
exists(AssignExpr a | a.getTarget().getAFlowNode() = this) exists(Py::AssignExpr a | this.getNode() = a.getTarget())
or or
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue())) exists(Py::AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
or or
exists(Alias a | a.getAsname().getAFlowNode() = this) exists(Py::Alias a | this.getNode() = a.getAsname())
or or
augstore(_, this) augstore(_, this)
or or
// `x, y = 1, 2` where LHS is a combination of list or tuples // `x, y = 1, 2` where LHS is a combination of list or tuples
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this) exists(Py::Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget()))
or or
exists(For for | for.getTarget().getAFlowNode() = this) exists(Py::For for | this.getNode() = for.getTarget())
or or
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault())) exists(Py::Parameter param | this.getNode() = param.asName() and exists(param.getDefault()))
} }
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */ /** flow node corresponding to the value assigned for the definition corresponding to this flow node */
ControlFlowNode getValue() { ControlFlowNode getValue() {
result = assigned_value(this.getNode()).getAFlowNode() and result.getNode() = assigned_value(this.getNode()) and
( (
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
or or
@@ -584,16 +584,16 @@ class DefinitionNode extends ControlFlowNode {
// since the default value for a parameter is evaluated in the same basic block as // since the default value for a parameter is evaluated in the same basic block as
// the function definition, but the parameter belongs to the basic block of the function, // the function definition, but the parameter belongs to the basic block of the function,
// there is no dominance relationship between the two. // there is no dominance relationship between the two.
exists(Parameter param | this = param.asName().getAFlowNode()) exists(Py::Parameter param | this.getNode() = param.asName())
) )
} }
} }
private Expr list_or_tuple_nested_element(Expr list_or_tuple) { private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) {
exists(Expr elt | exists(Py::Expr elt |
elt = list_or_tuple.(Tuple).getAnElt() elt = list_or_tuple.(Py::Tuple).getAnElt()
or or
elt = list_or_tuple.(List).getAnElt() elt = list_or_tuple.(Py::List).getAnElt()
| |
result = elt result = elt
or or
@@ -603,12 +603,12 @@ private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
/** /**
* A control flow node corresponding to a deletion statement, such as `del x`. * A control flow node corresponding to a deletion statement, such as `del x`.
* There can be multiple `DeletionNode`s for each `Delete` such that each * There can be multiple `DeletionNode`s for each `Py::Delete` such that each
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like: * target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
* `NameNode('a') -> DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`. * `NameNode('a') -> DeletionNode -> NameNode('b') -> AttrNode('y') -> DeletionNode`.
*/ */
class DeletionNode extends ControlFlowNode { class DeletionNode extends ControlFlowNode {
DeletionNode() { toAst(this) instanceof Delete } DeletionNode() { toAst(this) instanceof Py::Delete }
/** Gets the unique target of this deletion node. */ /** Gets the unique target of this deletion node. */
ControlFlowNode getTarget() { result.getASuccessor() = this } ControlFlowNode getTarget() { result.getASuccessor() = this }
@@ -617,9 +617,9 @@ class DeletionNode extends ControlFlowNode {
/** A control flow node corresponding to a sequence (tuple or list) literal */ /** A control flow node corresponding to a sequence (tuple or list) literal */
abstract class SequenceNode extends ControlFlowNode { abstract class SequenceNode extends ControlFlowNode {
SequenceNode() { SequenceNode() {
toAst(this) instanceof Tuple toAst(this) instanceof Py::Tuple
or or
toAst(this) instanceof List toAst(this) instanceof Py::List
} }
/** Gets the control flow node for an element of this sequence */ /** Gets the control flow node for an element of this sequence */
@@ -632,11 +632,11 @@ abstract class SequenceNode extends ControlFlowNode {
/** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */ /** A control flow node corresponding to a tuple expression such as `( 1, 3, 5, 7, 9 )` */
class TupleNode extends SequenceNode { class TupleNode extends SequenceNode {
TupleNode() { toAst(this) instanceof Tuple } TupleNode() { toAst(this) instanceof Py::Tuple }
override ControlFlowNode getElement(int n) { override ControlFlowNode getElement(int n) {
Stages::AST::ref() and Stages::AST::ref() and
exists(Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and exists(Py::Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and
( (
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
or or
@@ -647,10 +647,10 @@ class TupleNode extends SequenceNode {
/** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */ /** A control flow node corresponding to a list expression, such as `[ 1, 3, 5, 7, 9 ]` */
class ListNode extends SequenceNode { class ListNode extends SequenceNode {
ListNode() { toAst(this) instanceof List } ListNode() { toAst(this) instanceof Py::List }
override ControlFlowNode getElement(int n) { override ControlFlowNode getElement(int n) {
exists(List l | this.getNode() = l and result.getNode() = l.getElt(n)) and exists(Py::List l | this.getNode() = l and result.getNode() = l.getElt(n)) and
( (
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
or or
@@ -661,10 +661,10 @@ class ListNode extends SequenceNode {
/** A control flow node corresponding to a set expression, such as `{ 1, 3, 5, 7, 9 }` */ /** A control flow node corresponding to a set expression, such as `{ 1, 3, 5, 7, 9 }` */
class SetNode extends ControlFlowNode { class SetNode extends ControlFlowNode {
SetNode() { toAst(this) instanceof Set } SetNode() { toAst(this) instanceof Py::Set }
ControlFlowNode getAnElement() { ControlFlowNode getAnElement() {
exists(Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and exists(Py::Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and
( (
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
or or
@@ -675,20 +675,20 @@ class SetNode extends ControlFlowNode {
/** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */ /** A control flow node corresponding to a dictionary literal, such as `{ 'a': 1, 'b': 2 }` */
class DictNode extends ControlFlowNode { class DictNode extends ControlFlowNode {
DictNode() { toAst(this) instanceof Dict } DictNode() { toAst(this) instanceof Py::Dict }
/** /**
* Gets a key of this dictionary literal node, for those items that have keys * Gets a key of this dictionary literal node, for those items that have keys
* E.g, in {'a':1, **b} this returns only 'a' * E.g, in {'a':1, **b} this returns only 'a'
*/ */
ControlFlowNode getAKey() { ControlFlowNode getAKey() {
exists(Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
} }
/** Gets a value of this dictionary literal node */ /** Gets a value of this dictionary literal node */
ControlFlowNode getAValue() { ControlFlowNode getAValue() {
exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and exists(Py::Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
} }
} }
@@ -712,21 +712,23 @@ class IterableNode extends ControlFlowNode {
} }
} }
private AstNode assigned_value(Expr lhs) { private Py::AstNode assigned_value(Py::Expr lhs) {
/* lhs = result */ /* lhs = result */
exists(Assign a | a.getATarget() = lhs and result = a.getValue()) exists(Py::Assign a | a.getATarget() = lhs and result = a.getValue())
or or
/* lhs := result */ /* lhs := result */
exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue()) exists(Py::AssignExpr a | a.getTarget() = lhs and result = a.getValue())
or or
/* lhs : annotation = result */ /* lhs : annotation = result */
exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue()) exists(Py::AnnAssign a | a.getTarget() = lhs and result = a.getValue())
or or
/* import result as lhs */ /* import result as lhs */
exists(Alias a | a.getAsname() = lhs and result = a.getValue()) exists(Py::Alias a | a.getAsname() = lhs and result = a.getValue())
or or
/* lhs += x => result = (lhs + x) */ /* lhs += x => result = (lhs + x) */
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft()) exists(Py::AugAssign a, Py::BinaryExpr b |
b = a.getOperation() and result = b and lhs = b.getLeft()
)
or or
/* /*
* ..., lhs, ... = ..., result, ... * ..., lhs, ... = ..., result, ...
@@ -734,31 +736,31 @@ private AstNode assigned_value(Expr lhs) {
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ... * ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
*/ */
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) exists(Py::Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
or or
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */ /* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
result.(For).getTarget() = lhs result.(Py::For).getTarget() = lhs
or or
exists(Parameter param | lhs = param.asName() and result = param.getDefault()) exists(Py::Parameter param | lhs = param.asName() and result = param.getDefault())
} }
predicate nested_sequence_assign( predicate nested_sequence_assign(
Expr left_parent, Expr right_parent, Expr left_result, Expr right_result Py::Expr left_parent, Py::Expr right_parent, Py::Expr left_result, Py::Expr right_result
) { ) {
exists(Assign a | exists(Py::Assign a |
a.getATarget().getASubExpression*() = left_parent and a.getATarget().getASubExpression*() = left_parent and
a.getValue().getASubExpression*() = right_parent a.getValue().getASubExpression*() = right_parent
) and ) and
exists(int i, Expr left_elem, Expr right_elem | exists(int i, Py::Expr left_elem, Py::Expr right_elem |
( (
left_elem = left_parent.(Tuple).getElt(i) left_elem = left_parent.(Py::Tuple).getElt(i)
or or
left_elem = left_parent.(List).getElt(i) left_elem = left_parent.(Py::List).getElt(i)
) and ) and
( (
right_elem = right_parent.(Tuple).getElt(i) right_elem = right_parent.(Py::Tuple).getElt(i)
or or
right_elem = right_parent.(List).getElt(i) right_elem = right_parent.(Py::List).getElt(i)
) )
| |
left_result = left_elem and right_result = right_elem left_result = left_elem and right_result = right_elem
@@ -769,9 +771,9 @@ predicate nested_sequence_assign(
/** A flow node for a `for` statement. */ /** A flow node for a `for` statement. */
class ForNode extends ControlFlowNode { class ForNode extends ControlFlowNode {
ForNode() { toAst(this) instanceof For } ForNode() { toAst(this) instanceof Py::For }
override For getNode() { result = super.getNode() } override Py::For getNode() { result = super.getNode() }
/** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */ /** Holds if this `for` statement causes iteration over `sequence` storing each step of the iteration in `target` */
predicate iterates(ControlFlowNode target, ControlFlowNode sequence) { predicate iterates(ControlFlowNode target, ControlFlowNode sequence) {
@@ -782,7 +784,7 @@ class ForNode extends ControlFlowNode {
/** Gets the sequence node for this `for` statement. */ /** Gets the sequence node for this `for` statement. */
ControlFlowNode getSequence() { ControlFlowNode getSequence() {
exists(For for | exists(Py::For for |
toAst(this) = for and toAst(this) = for and
for.getIter() = result.getNode() for.getIter() = result.getNode()
| |
@@ -792,7 +794,7 @@ class ForNode extends ControlFlowNode {
/** A possible `target` for this `for` statement, not accounting for loop unrolling */ /** A possible `target` for this `for` statement, not accounting for loop unrolling */
private ControlFlowNode possibleTarget() { private ControlFlowNode possibleTarget() {
exists(For for | exists(Py::For for |
toAst(this) = for and toAst(this) = for and
for.getTarget() = result.getNode() and for.getTarget() = result.getNode() and
this.getBasicBlock().dominates(result.getBasicBlock()) this.getBasicBlock().dominates(result.getBasicBlock())
@@ -809,11 +811,11 @@ class ForNode extends ControlFlowNode {
/** A flow node for a `raise` statement */ /** A flow node for a `raise` statement */
class RaiseStmtNode extends ControlFlowNode { class RaiseStmtNode extends ControlFlowNode {
RaiseStmtNode() { toAst(this) instanceof Raise } RaiseStmtNode() { toAst(this) instanceof Py::Raise }
/** Gets the control flow node for the exception raised by this raise statement */ /** Gets the control flow node for the exception raised by this raise statement */
ControlFlowNode getException() { ControlFlowNode getException() {
exists(Raise r | exists(Py::Raise r |
r = toAst(this) and r = toAst(this) and
r.getException() = toAst(result) and r.getException() = toAst(result) and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
@@ -827,36 +829,36 @@ class RaiseStmtNode extends ControlFlowNode {
*/ */
class NameNode extends ControlFlowNode { class NameNode extends ControlFlowNode {
NameNode() { NameNode() {
exists(Name n | py_flow_bb_node(this, n, _, _)) exists(Py::Name n | py_flow_bb_node(this, n, _, _))
or or
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _)) exists(Py::PlaceHolder p | py_flow_bb_node(this, p, _, _))
} }
/** Whether this flow node defines the variable `v`. */ /** Whether this flow node defines the variable `v`. */
predicate defines(Variable v) { predicate defines(Py::Variable v) {
exists(Name d | this.getNode() = d and d.defines(v)) and exists(Py::Name d | this.getNode() = d and d.defines(v)) and
not this.isLoad() not this.isLoad()
} }
/** Whether this flow node deletes the variable `v`. */ /** Whether this flow node deletes the variable `v`. */
predicate deletes(Variable v) { exists(Name d | this.getNode() = d and d.deletes(v)) } predicate deletes(Py::Variable v) { exists(Py::Name d | this.getNode() = d and d.deletes(v)) }
/** Whether this flow node uses the variable `v`. */ /** Whether this flow node uses the variable `v`. */
predicate uses(Variable v) { predicate uses(Py::Variable v) {
this.isLoad() and this.isLoad() and
exists(Name u | this.getNode() = u and u.uses(v)) exists(Py::Name u | this.getNode() = u and u.uses(v))
or or
exists(PlaceHolder u | exists(Py::PlaceHolder u |
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Py::Load
) )
or or
Scopes::use_of_global_variable(this, v.getScope(), v.getId()) Scopes::use_of_global_variable(this, v.getScope(), v.getId())
} }
string getId() { string getId() {
result = this.getNode().(Name).getId() result = this.getNode().(Py::Name).getId()
or or
result = this.getNode().(PlaceHolder).getId() result = this.getNode().(Py::PlaceHolder).getId()
} }
/** Whether this is a use of a local variable. */ /** Whether this is a use of a local variable. */
@@ -868,82 +870,84 @@ class NameNode extends ControlFlowNode {
/** Whether this is a use of a global (including builtin) variable. */ /** Whether this is a use of a global (including builtin) variable. */
predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) } predicate isGlobal() { Scopes::use_of_global_variable(this, _, _) }
predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) } predicate isSelf() {
exists(Py::SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this)
}
} }
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */ /** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
class NameConstantNode extends NameNode { class NameConstantNode extends NameNode {
NameConstantNode() { exists(NameConstant n | py_flow_bb_node(this, n, _, _)) } NameConstantNode() { exists(Py::NameConstant n | py_flow_bb_node(this, n, _, _)) }
/* /*
* We ought to override uses as well, but that has * We ought to override uses as well, but that has
* a serious performance impact. * a serious performance impact.
* deprecated predicate uses(Variable v) { none() } * deprecated predicate uses(Py::Variable v) { none() }
*/ */
} }
/** A control flow node corresponding to a starred expression, `*a`. */ /** A control flow node corresponding to a starred expression, `*a`. */
class StarredNode extends ControlFlowNode { class StarredNode extends ControlFlowNode {
StarredNode() { toAst(this) instanceof Starred } StarredNode() { toAst(this) instanceof Py::Starred }
ControlFlowNode getValue() { toAst(result) = toAst(this).(Starred).getValue() } ControlFlowNode getValue() { toAst(result) = toAst(this).(Py::Starred).getValue() }
} }
/** The ControlFlowNode for an 'except' statement. */ /** The ControlFlowNode for an 'except' statement. */
class ExceptFlowNode extends ControlFlowNode { class ExceptFlowNode extends ControlFlowNode {
ExceptFlowNode() { this.getNode() instanceof ExceptStmt } ExceptFlowNode() { this.getNode() instanceof Py::ExceptStmt }
/** /**
* Gets the type handled by this exception handler. * Gets the type handled by this exception handler.
* `ExceptionType` in `except ExceptionType as e:` * `Py::ExceptionType` in `except Py::ExceptionType as e:`
*/ */
ControlFlowNode getType() { ControlFlowNode getType() {
exists(ExceptStmt ex | exists(Py::ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and ex = this.getNode() and
result = ex.getType().getAFlowNode() result.getNode() = ex.getType()
) )
} }
/** /**
* Gets the name assigned to the handled exception, if any. * Gets the name assigned to the handled exception, if any.
* `e` in `except ExceptionType as e:` * `e` in `except Py::ExceptionType as e:`
*/ */
ControlFlowNode getName() { ControlFlowNode getName() {
exists(ExceptStmt ex | exists(Py::ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and ex = this.getNode() and
result = ex.getName().getAFlowNode() result.getNode() = ex.getName()
) )
} }
} }
/** The ControlFlowNode for an 'except*' statement. */ /** The ControlFlowNode for an 'except*' statement. */
class ExceptGroupFlowNode extends ControlFlowNode { class ExceptGroupFlowNode extends ControlFlowNode {
ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt } ExceptGroupFlowNode() { this.getNode() instanceof Py::ExceptGroupStmt }
/** /**
* Gets the type handled by this exception handler. * Gets the type handled by this exception handler.
* `ExceptionType` in `except* ExceptionType as e:` * `Py::ExceptionType` in `except* Py::ExceptionType as e:`
*/ */
ControlFlowNode getType() { ControlFlowNode getType() {
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode() result.getNode() = this.getNode().(Py::ExceptGroupStmt).getType()
} }
/** /**
* Gets the name assigned to the handled exception, if any. * Gets the name assigned to the handled exception, if any.
* `e` in `except* ExceptionType as e:` * `e` in `except* Py::ExceptionType as e:`
*/ */
ControlFlowNode getName() { ControlFlowNode getName() {
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode() result.getNode() = this.getNode().(Py::ExceptGroupStmt).getName()
} }
} }
private module Scopes { private module Scopes {
private predicate fast_local(NameNode n) { private predicate fast_local(NameNode n) {
exists(FastLocalVariable v | exists(Py::FastLocalVariable v |
n.uses(v) and n.uses(v) and
v.getScope() = n.getScope() v.getScope() = n.getScope()
) )
@@ -952,15 +956,15 @@ private module Scopes {
predicate local(NameNode n) { predicate local(NameNode n) {
fast_local(n) fast_local(n)
or or
exists(SsaVariable var | exists(Py::SsaVariable var |
var.getAUse() = n and var.getAUse() = n and
n.getScope() instanceof Class and n.getScope() instanceof Py::Class and
exists(var.getDefinition()) exists(var.getDefinition())
) )
} }
predicate non_local(NameNode n) { predicate non_local(NameNode n) {
exists(FastLocalVariable flv | exists(Py::FastLocalVariable flv |
flv.getALoad() = n.getNode() and flv.getALoad() = n.getNode() and
not flv.getScope() = n.getScope() not flv.getScope() = n.getScope()
) )
@@ -968,20 +972,20 @@ private module Scopes {
// magic is fine, but we get questionable join-ordering of it // magic is fine, but we get questionable join-ordering of it
pragma[nomagic] pragma[nomagic]
predicate use_of_global_variable(NameNode n, Module scope, string name) { predicate use_of_global_variable(NameNode n, Py::Module scope, string name) {
n.isLoad() and n.isLoad() and
not non_local(n) and not non_local(n) and
not exists(SsaVariable var | var.getAUse() = n | not exists(Py::SsaVariable var | var.getAUse() = n |
var.getVariable() instanceof FastLocalVariable var.getVariable() instanceof Py::FastLocalVariable
or or
n.getScope() instanceof Class and n.getScope() instanceof Py::Class and
not maybe_undefined(var) not maybe_undefined(var)
) and ) and
name = n.getId() and name = n.getId() and
scope = n.getEnclosingModule() scope = n.getEnclosingModule()
} }
private predicate maybe_undefined(SsaVariable var) { private predicate maybe_undefined(Py::SsaVariable var) {
not exists(var.getDefinition()) and not py_ssa_phi(var, _) not exists(var.getDefinition()) and not py_ssa_phi(var, _)
or or
var.getDefinition().isDelete() var.getDefinition().isDelete()
@@ -1058,13 +1062,13 @@ class BasicBlock extends @py_flow_node {
private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() } private predicate oneNodeBlock() { this.firstNode() = this.getLastNode() }
private predicate startLocationInfo(string file, int line, int col) { private predicate startLocationInfo(string file, int line, int col) {
if this.firstNode().getNode() instanceof Scope if this.firstNode().getNode() instanceof Py::Scope
then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _) then this.firstNode().getASuccessor().getLocation().hasLocationInfo(file, line, col, _, _)
else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _) else this.firstNode().getLocation().hasLocationInfo(file, line, col, _, _)
} }
private predicate endLocationInfo(int endl, int endc) { private predicate endLocationInfo(int endl, int endc) {
if this.getLastNode().getNode() instanceof Scope and not this.oneNodeBlock() if this.getLastNode().getNode() instanceof Py::Scope and not this.oneNodeBlock()
then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc) then this.getLastNode().getAPredecessor().getLocation().hasLocationInfo(_, _, _, endl, endc)
else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc) else this.getLastNode().getLocation().hasLocationInfo(_, _, _, endl, endc)
} }
@@ -1081,7 +1085,7 @@ class BasicBlock extends @py_flow_node {
/** Whether flow from this basic block reaches a normal exit from its scope */ /** Whether flow from this basic block reaches a normal exit from its scope */
predicate reachesExit() { predicate reachesExit() {
exists(Scope s | s.getANormalExit().getBasicBlock() = this) exists(Py::Scope s | s.getANormalExit().getBasicBlock() = this)
or or
this.getASuccessor().reachesExit() this.getASuccessor().reachesExit()
} }
@@ -1122,7 +1126,7 @@ class BasicBlock extends @py_flow_node {
/** Gets the scope of this block */ /** Gets the scope of this block */
pragma[nomagic] pragma[nomagic]
Scope getScope() { Py::Scope getScope() {
exists(ControlFlowNode n | n.getBasicBlock() = this | exists(ControlFlowNode n | n.getBasicBlock() = this |
/* Take care not to use an entry or exit node as that node's scope will be the outer scope */ /* Take care not to use an entry or exit node as that node's scope will be the outer scope */
not py_scope_flow(n, _, -1) and not py_scope_flow(n, _, -1) and
@@ -1145,17 +1149,17 @@ class BasicBlock extends @py_flow_node {
predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) } predicate reaches(BasicBlock other) { this = other or this.strictlyReaches(other) }
/** /**
* Gets the `ConditionBlock`, if any, that controls this block and * Gets the `Py::ConditionBlock`, if any, that controls this block and
* does not control any other `ConditionBlock`s that control this block. * does not control any other `Py::ConditionBlock`s that control this block.
* That is the `ConditionBlock` that is closest dominator. * That is the `Py::ConditionBlock` that is closest dominator.
*/ */
ConditionBlock getImmediatelyControllingBlock() { Py::ConditionBlock getImmediatelyControllingBlock() {
result = this.nonControllingImmediateDominator*().getImmediateDominator() result = this.nonControllingImmediateDominator*().getImmediateDominator()
} }
private BasicBlock nonControllingImmediateDominator() { private BasicBlock nonControllingImmediateDominator() {
result = this.getImmediateDominator() and result = this.getImmediateDominator() and
not result.(ConditionBlock).controls(this, _) not result.(Py::ConditionBlock).controls(this, _)
} }
/** /**
@@ -1175,7 +1179,7 @@ private class ControlFlowNodeAlias = ControlFlowNode;
final private class FinalBasicBlock = BasicBlock; final private class FinalBasicBlock = BasicBlock;
module Cfg implements BB::CfgSig<Location> { module Cfg implements BB::CfgSig<Py::Location> {
private import codeql.controlflow.SuccessorType private import codeql.controlflow.SuccessorType
class ControlFlowNode = ControlFlowNodeAlias; class ControlFlowNode = ControlFlowNodeAlias;
@@ -1186,7 +1190,7 @@ module Cfg implements BB::CfgSig<Location> {
// Using the location of the first node is simple // Using the location of the first node is simple
// and we just need a way to identify the basic block // and we just need a way to identify the basic block
// during debugging, so this will be serviceable. // during debugging, so this will be serviceable.
Location getLocation() { result = super.getNode(0).getLocation() } Py::Location getLocation() { result = super.getNode(0).getLocation() }
int length() { result = count(int i | exists(this.getNode(i))) } int length() { result = count(int i | exists(this.getNode(i))) }

View File

@@ -153,8 +153,16 @@ class Function extends Function_, Scope, AstNode {
override predicate contains(AstNode inner) { Scope.super.contains(inner) } override predicate contains(AstNode inner) { Scope.super.contains(inner) }
/** Gets a control flow node for a return value of this function */ /**
ControlFlowNode getAReturnValueFlowNode() { * DEPRECATED: bind a `Return` node explicitly instead, e.g.
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
* This API is being phased out together with `AstNode.getAFlowNode()` to
* untangle the AST and CFG hierarchies in preparation for migrating the
* dataflow library off the legacy CFG.
*
* Gets a control flow node for a return value of this function.
*/
deprecated ControlFlowNode getAReturnValueFlowNode() {
exists(Return ret | exists(Return ret |
ret.getScope() = this and ret.getScope() = this and
ret.getValue() = result.getNode() ret.getValue() = result.getNode()

View File

@@ -163,7 +163,7 @@ class ImportMember extends ImportMember_ {
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
} }
override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } deprecated override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** An import statement */ /** An import statement */

View File

@@ -46,20 +46,23 @@ class SelfAttributeRead extends SelfAttribute {
} }
predicate guardedByHasattr() { predicate guardedByHasattr() {
exists(Variable var, ControlFlowNode n | exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
var.getAUse() = this.getObject().getAFlowNode() and this_.getNode() = this and obj_.getNode() = this.getObject()
|
var.getAUse() = obj_ and
hasattr(n, var.getAUse(), this.getName()) and hasattr(n, var.getAUse(), this.getName()) and
n.strictlyDominates(this.getAFlowNode()) n.strictlyDominates(this_)
) )
} }
pragma[noinline] pragma[noinline]
predicate locallyDefined() { predicate locallyDefined() {
exists(SelfAttributeStore store | exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
this.getName() = store.getName() and store_.getNode() = store and this_.getNode() = this
this.getScope() = store.getScope()
| |
store.getAFlowNode().strictlyDominates(this.getAFlowNode()) this.getName() = store.getName() and
this.getScope() = store.getScope() and
store_.strictlyDominates(this_)
) )
} }
} }

View File

@@ -5,24 +5,30 @@ private import semmle.python.dataflow.new.DataFlow
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
exists(CompareNode cn | cn = g | exists(CompareNode cn | cn = g |
exists(ImmutableLiteral const, Cmpop op | exists(ImmutableLiteral const, Cmpop op, ControlFlowNode c |
op = any(Eq eq) and branch = true c.getNode() = const and
or (
op = any(NotEq ne) and branch = false op = any(Eq eq) and branch = true
or
op = any(NotEq ne) and branch = false
)
| |
cn.operands(const.getAFlowNode(), op, node) cn.operands(c, op, node)
or or
cn.operands(node, op, const.getAFlowNode()) cn.operands(node, op, c)
) )
or or
exists(NameConstant const, Cmpop op | exists(NameConstant const, Cmpop op, ControlFlowNode c |
op = any(Is is_) and branch = true c.getNode() = const and
or (
op = any(IsNot isn) and branch = false op = any(Is is_) and branch = true
or
op = any(IsNot isn) and branch = false
)
| |
cn.operands(const.getAFlowNode(), op, node) cn.operands(c, op, node)
or or
cn.operands(node, op, const.getAFlowNode()) cn.operands(node, op, c)
) )
or or
exists(IterableNode const_iterable, Cmpop op | exists(IterableNode const_iterable, Cmpop op |

View File

@@ -228,7 +228,7 @@ private class ClassDefinitionAsAttrWrite extends AttrWrite, CfgNode {
override Node getValue() { result.asCfgNode() = node.getValue() } override Node getValue() { result.asCfgNode() = node.getValue() }
override Node getObject() { result.asCfgNode() = cls.getAFlowNode() } override Node getObject() { result.asCfgNode().getNode() = cls }
override ExprNode getAttributeNameExpr() { none() } override ExprNode getAttributeNameExpr() { none() }

View File

@@ -1913,8 +1913,8 @@ abstract class ReturnNode extends Node {
class ExtractedReturnNode extends ReturnNode, CfgNode { class ExtractedReturnNode extends ReturnNode, CfgNode {
// See `TaintTrackingImplementation::returnFlowStep` // See `TaintTrackingImplementation::returnFlowStep`
ExtractedReturnNode() { ExtractedReturnNode() {
node = any(Return ret).getValue().getAFlowNode() or node.getNode() = any(Return ret).getValue() or
node = any(Yield yield).getAFlowNode() node.getNode() = any(Yield yield)
} }
override ReturnKind getKind() { any() } override ReturnKind getKind() { any() }
@@ -1932,7 +1932,7 @@ class ExtractedReturnNode extends ReturnNode, CfgNode {
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode { class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
YieldNodeInContextManagerFunction() { YieldNodeInContextManagerFunction() {
hasContextmanagerDecorator(node.getScope()) and hasContextmanagerDecorator(node.getScope()) and
node = any(Yield yield).getValue().getAFlowNode() node.getNode() = any(Yield yield).getValue()
} }
override ReturnKind getKind() { any() } override ReturnKind getKind() { any() }

View File

@@ -185,8 +185,8 @@ private predicate synthDictSplatArgumentNodeStoreStep(
*/ */
predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) { predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) {
exists(Yield yield | exists(Yield yield |
nodeTo.asCfgNode() = yield.getAFlowNode() and nodeTo.asCfgNode().getNode() = yield and
nodeFrom.asCfgNode() = yield.getValue().getAFlowNode() and nodeFrom.asCfgNode().getNode() = yield.getValue() and
// TODO: Consider if this will also need to transfer dictionary content // TODO: Consider if this will also need to transfer dictionary content
// once dictionary comprehensions are supported. // once dictionary comprehensions are supported.
c instanceof ListElementContent c instanceof ListElementContent

View File

@@ -485,7 +485,7 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
/** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */ /** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */
Node getALocalRead() { Node getALocalRead() {
result.asCfgNode() = var.getALoad().getAFlowNode() and result.asCfgNode().getNode() = var.getALoad() and
not result.getScope() = mod not result.getScope() = mod
} }

View File

@@ -9,7 +9,19 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.ImportStar private import semmle.python.dataflow.new.internal.ImportStar
private import semmle.python.dataflow.new.TypeTracking private import semmle.python.dataflow.new.TypeTracking
private import semmle.python.dataflow.new.internal.DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate
private import semmle.python.essa.SsaDefinitions
/**
* Holds if `init` is a package's `__init__.py` and `var` is a global variable in
* `init` whose name matches a submodule of the package.
*
* Inlined from `SsaSource::init_module_submodule_defn` to avoid pulling
* `semmle.python.essa.SsaDefinitions` into the new dataflow stack.
*/
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
init.isPackageInit() and
exists(init.getPackage().getSubModule(var.getId())) and
var.getScope() = init
}
/** /**
* Python modules and the way imports are resolved are... complicated. Here's a crash course in how * Python modules and the way imports are resolved are... complicated. Here's a crash course in how
@@ -326,7 +338,7 @@ module ImportResolution {
// imported yet. // imported yet.
exists(string submodule, Module package, EssaVariable var | exists(string submodule, Module package, EssaVariable var |
submodule = var.getName() and submodule = var.getName() and
SsaSource::init_module_submodule_defn(var.getSourceVariable(), package.getEntryNode()) and initModuleSubmoduleDefn(var.getSourceVariable(), package) and
m = getModuleFromName(package.getPackageName() + "." + submodule) and m = getModuleFromName(package.getPackageName() + "." + submodule) and
result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode() result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
) )

View File

@@ -94,8 +94,10 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
Node returnOf(Node callable, SummaryComponent return) { Node returnOf(Node callable, SummaryComponent return) {
return = FlowSummaryImpl::Private::SummaryComponent::return() and return = FlowSummaryImpl::Private::SummaryComponent::return() and
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable` // `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
result.asCfgNode() = exists(Return ret |
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode() ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
result.asCfgNode().getNode() = ret.getValue()
)
} }
// Relating callables to nodes // Relating callables to nodes

View File

@@ -61,7 +61,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
class VariableWrite extends ControlFlowNode { class VariableWrite extends ControlFlowNode {
CapturedVariable v; CapturedVariable v;
VariableWrite() { this = v.getAStore().getAFlowNode().(DefinitionNode).getValue() } VariableWrite() { exists(DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue()) }
CapturedVariable getVariable() { result = v } CapturedVariable getVariable() { result = v }
@@ -71,7 +71,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
class VariableRead extends Expr { class VariableRead extends Expr {
CapturedVariable v; CapturedVariable v;
VariableRead() { this = v.getALoad().getAFlowNode() } VariableRead() { this.getNode() = v.getALoad() }
CapturedVariable getVariable() { result = v } CapturedVariable getVariable() { result = v }
} }

View File

@@ -448,8 +448,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
context = TNoParam() and context = TNoParam() and
src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and
node.asCfgNode() = call and node.asCfgNode() = call and
retval.asCfgNode() = retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
) and ) and
edgeLabel = "return" edgeLabel = "return"
} }
@@ -471,8 +470,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
this.callContexts(call, src, pyfunc, context, callee) and this.callContexts(call, src, pyfunc, context, callee) and
retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and
node.asCfgNode() = call and node.asCfgNode() = call and
retval.asCfgNode() = retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
) and ) and
edgeLabel = "call" edgeLabel = "call"
} }
@@ -716,8 +714,10 @@ private class EssaTaintTracking extends string instanceof TaintTracking::Configu
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
path.noAttribute() path.noAttribute()
| |
assign.getValue().getAFlowNode() = srcnode.asCfgNode() and srcnode.asCfgNode().getNode() = assign.getValue() and
depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and exists(SequenceNode left_parent | left_parent.getNode() = assign.getATarget() |
depth = iterable_unpacking_descent(left_parent, defn.getDefiningNode())
) and
kind = taint_at_depth(srckind, depth) kind = taint_at_depth(srckind, depth)
) )
} }
@@ -964,7 +964,7 @@ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) {
* - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1 * - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1
*/ */
int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) { int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) {
exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and exists(Assign a | left_parent.getNode() = a.getATarget().getASubExpression*()) and
left_parent.getAnElement() = left_defn and left_parent.getAnElement() = left_defn and
// Handle `a, *b = some_iterable` // Handle `a, *b = some_iterable`
if left_defn instanceof StarredNode then result = 0 else result = 1 if left_defn instanceof StarredNode then result = 0 else result = 1

View File

@@ -56,7 +56,7 @@ module SsaSource {
predicate with_definition(Variable v, ControlFlowNode defn) { predicate with_definition(Variable v, ControlFlowNode defn) {
exists(With with, Name var | exists(With with, Name var |
with.getOptionalVars() = var and with.getOptionalVars() = var and
var.getAFlowNode() = defn defn.getNode() = var
| |
var = v.getAStore() var = v.getAStore()
) )
@@ -67,7 +67,7 @@ module SsaSource {
predicate pattern_capture_definition(Variable v, ControlFlowNode defn) { predicate pattern_capture_definition(Variable v, ControlFlowNode defn) {
exists(MatchCapturePattern capture, Name var | exists(MatchCapturePattern capture, Name var |
capture.getVariable() = var and capture.getVariable() = var and
var.getAFlowNode() = defn defn.getNode() = var
| |
var = v.getAStore() var = v.getAStore()
) )
@@ -78,7 +78,7 @@ module SsaSource {
predicate pattern_alias_definition(Variable v, ControlFlowNode defn) { predicate pattern_alias_definition(Variable v, ControlFlowNode defn) {
exists(MatchAsPattern pattern, Name var | exists(MatchAsPattern pattern, Name var |
pattern.getAlias() = var and pattern.getAlias() = var and
var.getAFlowNode() = defn defn.getNode() = var
| |
var = v.getAStore() var = v.getAStore()
) )

View File

@@ -59,7 +59,7 @@ module Bottle {
override Parameter getARoutedParameter() { none() } override Parameter getARoutedParameter() { none() }
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node } override Function getARequestHandler() { node.getNode() = result.getADecorator() }
} }
} }
@@ -73,7 +73,10 @@ module Bottle {
/** A response returned by a view callable. */ /** A response returned by a view callable. */
class BottleReturnResponse extends Http::Server::HttpResponse::Range { class BottleReturnResponse extends Http::Server::HttpResponse::Range {
BottleReturnResponse() { BottleReturnResponse() {
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() exists(Return ret |
ret.getScope() = any(View::ViewCallable vc) and
this.asCfgNode().getNode() = ret.getValue()
)
} }
override DataFlow::Node getBody() { result = this } override DataFlow::Node getBody() { result = this }

View File

@@ -2872,7 +2872,10 @@ module PrivateDjango {
DataFlow::CfgNode DataFlow::CfgNode
{ {
DjangoRedirectViewGetRedirectUrlReturn() { DjangoRedirectViewGetRedirectUrlReturn() {
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode() exists(Return ret |
ret.getScope() = any(GetRedirectUrlFunction f) and
node.getNode() = ret.getValue()
)
} }
override DataFlow::Node getRedirectLocation() { result = this } override DataFlow::Node getRedirectLocation() { result = this }

View File

@@ -129,7 +129,7 @@ module FastApi {
result in [this.getArg(0), this.getArgByName("path")] result in [this.getArg(0), this.getArgByName("path")]
} }
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node } override Function getARequestHandler() { node.getNode() = result.getADecorator() }
override string getFramework() { result = "FastAPI" } override string getFramework() { result = "FastAPI" }
@@ -309,7 +309,10 @@ module FastApi {
FastApiRouteSetup routeSetup; FastApiRouteSetup routeSetup;
FastApiRequestHandlerReturn() { FastApiRequestHandlerReturn() {
node = routeSetup.getARequestHandler().getAReturnValueFlowNode() exists(Return ret |
ret.getScope() = routeSetup.getARequestHandler() and
node.getNode() = ret.getValue()
)
} }
override DataFlow::Node getBody() { result = this } override DataFlow::Node getBody() { result = this }

View File

@@ -371,7 +371,7 @@ module Flask {
result in [this.getArg(0), this.getArgByName("rule")] result in [this.getArg(0), this.getArgByName("rule")]
} }
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node } override Function getARequestHandler() { node.getNode() = result.getADecorator() }
} }
/** /**
@@ -536,7 +536,7 @@ module Flask {
FlaskRouteHandlerReturn() { FlaskRouteHandlerReturn() {
exists(Function routeHandler | exists(Function routeHandler |
routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and
node = routeHandler.getAReturnValueFlowNode() and exists(Return ret | ret.getScope() = routeHandler and node.getNode() = ret.getValue()) and
not this instanceof Flask::Response::InstanceSource not this instanceof Flask::Response::InstanceSource
) )
} }

View File

@@ -38,7 +38,7 @@ private module FlaskAdmin {
result in [this.getArg(0), this.getArgByName("url")] result in [this.getArg(0), this.getArgByName("url")]
} }
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node } override Function getARequestHandler() { node.getNode() = result.getADecorator() }
} }
/** /**
@@ -71,7 +71,7 @@ private module FlaskAdmin {
override Function getARequestHandler() { override Function getARequestHandler() {
exists(Flask::FlaskViewClass cls | exists(Flask::FlaskViewClass cls |
cls.getADecorator().getAFlowNode() = node and node.getNode() = cls.getADecorator() and
result = cls.getARequestHandler() result = cls.getARequestHandler()
) )
} }

View File

@@ -166,7 +166,10 @@ module Pyramid {
/** A response returned by a view callable. */ /** A response returned by a view callable. */
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range { private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
PyramidReturnResponse() { PyramidReturnResponse() {
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and exists(Return ret |
ret.getScope() = any(View::ViewCallable vc) and
this.asCfgNode().getNode() = ret.getValue()
) and
not this = instance() not this = instance()
} }

View File

@@ -2254,8 +2254,9 @@ module StdlibPrivate {
DataFlow::CfgNode DataFlow::CfgNode
{ {
WsgirefSimpleServerApplicationReturn() { WsgirefSimpleServerApplicationReturn() {
exists(WsgirefSimpleServerApplication requestHandler | exists(WsgirefSimpleServerApplication requestHandler, Return ret |
node = requestHandler.getAReturnValueFlowNode() ret.getScope() = requestHandler and
node.getNode() = ret.getValue()
) )
} }

View File

@@ -182,7 +182,10 @@ private module Twisted {
DataFlow::CfgNode DataFlow::CfgNode
{ {
TwistedResourceRenderMethodReturn() { TwistedResourceRenderMethodReturn() {
this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode() exists(Return ret |
ret.getScope() = any(TwistedResourceRenderMethod meth) and
this.asCfgNode().getNode() = ret.getValue()
)
} }
override DataFlow::Node getBody() { result = this } override DataFlow::Node getBody() { result = this }

View File

@@ -77,7 +77,7 @@ module Stages {
or or
exists(any(AstExtended::AstNode n).getParentNode()) exists(any(AstExtended::AstNode n).getParentNode())
or or
exists(any(AstExtended::AstNode n).getAFlowNode()) exists(PyFlow::ControlFlowNode cfg, AstExtended::AstNode n | cfg.getNode() = n)
or or
exists(any(PyFlow::BasicBlock b).getImmediateDominator()) exists(any(PyFlow::BasicBlock b).getImmediateDominator())
or or

View File

@@ -56,8 +56,9 @@ abstract class CallableObjectInternal extends ObjectInternal {
/** A Python function. */ /** A Python function. */
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject { class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
override Function getScope() { override Function getScope() {
exists(CallableExpr expr | exists(CallableExpr expr, ControlFlowNode exprCfg |
this = TPythonFunctionObject(expr.getAFlowNode()) and exprCfg.getNode() = expr and
this = TPythonFunctionObject(exprCfg) and
result = expr.getInnerScope() result = expr.getInnerScope()
) )
} }
@@ -80,11 +81,12 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
pragma[nomagic] pragma[nomagic]
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin | exists(Function func, Return ret, ControlFlowNode rval, ControlFlowNode forigin |
func = this.getScope() and func = this.getScope() and
callee.appliesToScope(func) callee.appliesToScope(func)
| |
rval = func.getAReturnValueFlowNode() and ret.getScope() = func and
rval.getNode() = ret.getValue() and
PointsToInternal::pointsTo(rval, callee, obj, forigin) and PointsToInternal::pointsTo(rval, callee, obj, forigin) and
origin = CfgOrigin::fromCfgNode(forigin) origin = CfgOrigin::fromCfgNode(forigin)
) )
@@ -160,10 +162,11 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
} }
private BasicBlock blockReturningNone(Function func) { private BasicBlock blockReturningNone(Function func) {
exists(Return ret | exists(Return ret, ControlFlowNode ret_ |
not exists(ret.getValue()) and not exists(ret.getValue()) and
ret.getScope() = func and ret.getScope() = func and
result = ret.getAFlowNode().getBasicBlock() ret_.getNode() = ret and
result = ret_.getBasicBlock()
) )
} }

View File

@@ -113,8 +113,9 @@ abstract class ClassObjectInternal extends ObjectInternal {
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject { class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
/** Gets the scope for this Python class */ /** Gets the scope for this Python class */
Class getScope() { Class getScope() {
exists(ClassExpr expr | exists(ClassExpr expr, ControlFlowNode exprCfg |
this = TPythonClassObject(expr.getAFlowNode()) and exprCfg.getNode() = expr and
this = TPythonClassObject(exprCfg) and
result = expr.getInnerScope() result = expr.getInnerScope()
) )
} }

View File

@@ -745,7 +745,12 @@ class PythonFunctionValue extends FunctionValue {
override int maxParameters() { result = this.getScope().getMaxPositionalArguments() } override int maxParameters() { result = this.getScope().getMaxPositionalArguments() }
/** Gets a control flow node corresponding to a return statement in this function */ /** Gets a control flow node corresponding to a return statement in this function */
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } ControlFlowNode getAReturnedNode() {
exists(Return ret |
ret.getScope() = this.getScope() and
result.getNode() = ret.getValue()
)
}
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }

View File

@@ -387,7 +387,7 @@ private PythonClassObjectInternal abcMetaClassObject() {
private predicate neither_class_nor_static_method(Function f) { private predicate neither_class_nor_static_method(Function f) {
not exists(f.getADecorator()) not exists(f.getADecorator())
or or
exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() | exists(ControlFlowNode deco | deco.getNode() = f.getADecorator() |
exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) | exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) |
o != ObjectInternal::staticMethod() and o != ObjectInternal::staticMethod() and
o != ObjectInternal::classMethod() o != ObjectInternal::classMethod()

View File

@@ -711,7 +711,7 @@ private module InterModulePointsTo {
ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin
) { ) {
exists(string name, ImportExpr i | exists(string name, ImportExpr i |
i.getAFlowNode() = f and f.getNode() = i and
i.getImportedModuleName() = name and i.getImportedModuleName() = name and
PointsToInternal::module_imported_as(value, name) and PointsToInternal::module_imported_as(value, name) and
origin = f and origin = f and
@@ -2118,8 +2118,9 @@ module Types {
result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0 result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0
or or
exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() | exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() |
exists(ObjectInternal base | exists(ObjectInternal base, ControlFlowNode baseNode |
PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _) baseNode.getNode() = pycls.getBase(n) and
PointsToInternal::pointsTo(baseNode, _, base, _)
| |
result = base and base != ObjectInternal::unknown() result = base and base != ObjectInternal::unknown()
or or
@@ -2223,7 +2224,10 @@ module Types {
} }
private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) {
result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction() exists(CallNode deco |
deco.getNode() = cls.getScope().getADecorator() and
result = deco.getFunction()
)
} }
private boolean has_six_add_metaclass(PythonClassObjectInternal cls) { private boolean has_six_add_metaclass(PythonClassObjectInternal cls) {
@@ -2262,7 +2266,7 @@ module Types {
} }
private EssaVariable metaclass_var(Class cls) { private EssaVariable metaclass_var(Class cls) {
result.getASourceUse() = cls.getMetaClass().getAFlowNode() result.getASourceUse().getNode() = cls.getMetaClass()
or or
major_version() = 2 and major_version() = 2 and
not exists(cls.getMetaClass()) and not exists(cls.getMetaClass()) and

View File

@@ -181,7 +181,7 @@ class ClassObject extends Object {
) )
} }
ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() } ControlFlowNode declaredMetaClass() { result.getNode() = this.getPyClass().getMetaClass() }
/** Has type inference failed to compute the full class hierarchy for this class for the reason given. */ /** Has type inference failed to compute the full class hierarchy for this class for the reason given. */
predicate failedInference(string reason) { Types::failedInference(this.theClass(), reason) } predicate failedInference(string reason) { Types::failedInference(this.theClass(), reason) }
@@ -195,8 +195,9 @@ class ClassObject extends Object {
* It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject. * It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject.
*/ */
Object getProbableSingletonInstance() { Object getProbableSingletonInstance() {
exists(ControlFlowNodeWithPointsTo use, Expr origin | exists(ControlFlowNodeWithPointsTo use, Expr origin, ControlFlowNode origin_ |
use.refersTo(result, this, origin.getAFlowNode()) origin_.getNode() = origin and
use.refersTo(result, this, origin_)
| |
this.hasStaticallyUniqueInstance() and this.hasStaticallyUniqueInstance() and
/* Ensure that original expression will be executed only one. */ /* Ensure that original expression will be executed only one. */

View File

@@ -427,7 +427,7 @@ class ExceptFlowNodeWithPointsTo extends ExceptFlowNode {
} }
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) { private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode()) exists(Tuple t | t = tuple.getOrigin() and result.getNode() = t.getAnElt())
} }
/** /**

View File

@@ -36,8 +36,8 @@ class RangeIterationVariableFact extends PointsToExtension {
RangeIterationVariableFact() { RangeIterationVariableFact() {
exists(For f, ControlFlowNode iterable | exists(For f, ControlFlowNode iterable |
iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and
f.getIter().getAFlowNode() = iterable and iterable.getNode() = f.getIter() and
f.getTarget().getAFlowNode() = this and this.(ControlFlowNode).getNode() = f.getTarget() and
exists(ObjectInternal range | exists(ObjectInternal range |
PointsTo::pointsTo(iterable, _, range, _) and PointsTo::pointsTo(iterable, _, range, _) and
range.getClass() = ObjectInternal::builtin("range") range.getClass() = ObjectInternal::builtin("range")

View File

@@ -137,7 +137,10 @@ class PyFunctionObject extends FunctionObject {
/** Gets a control flow node corresponding to the value of a return statement */ /** Gets a control flow node corresponding to the value of a return statement */
ControlFlowNodeWithPointsTo getAReturnedNode() { ControlFlowNodeWithPointsTo getAReturnedNode() {
result = this.getFunction().getAReturnValueFlowNode() exists(Return ret |
ret.getScope() = this.getFunction() and
result.getNode() = ret.getValue()
)
} }
override string descriptiveString() { override string descriptiveString() {
@@ -170,7 +173,7 @@ class PyFunctionObject extends FunctionObject {
predicate unconditionallyReturnsParameter(int n) { predicate unconditionallyReturnsParameter(int n) {
exists(SsaVariable pvar | exists(SsaVariable pvar |
exists(Parameter p | p = this.getFunction().getArg(n) | exists(Parameter p | p = this.getFunction().getArg(n) |
p.asName().getAFlowNode() = pvar.getDefinition() pvar.getDefinition().getNode() = p.asName()
) and ) and
exists(NameNode rval | exists(NameNode rval |
rval = pvar.getAUse() and rval = pvar.getAUse() and

View File

@@ -337,7 +337,7 @@ class TupleObject extends SequenceObject {
or or
this instanceof TupleNode this instanceof TupleNode
or or
exists(Function func | func.getVararg().getAFlowNode() = this) exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
} }
} }
@@ -352,7 +352,9 @@ module TupleObject {
} }
class NonEmptyTupleObject extends TupleObject { class NonEmptyTupleObject extends TupleObject {
NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) } NonEmptyTupleObject() {
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
}
override boolean booleanValue() { result = true } override boolean booleanValue() { result = true }
} }

View File

@@ -48,9 +48,11 @@ class CheckClass extends ClassObject {
self_dict = sub.getObject() self_dict = sub.getObject()
or or
/* Indirect assignment via temporary variable */ /* Indirect assignment via temporary variable */
exists(SsaVariable v | exists(SsaVariable v, ControlFlowNode subObjCfg, ControlFlowNode selfDictCfg |
v.getAUse() = sub.getObject().getAFlowNode() and subObjCfg.getNode() = sub.getObject() and selfDictCfg.getNode() = self_dict
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode() |
v.getAUse() = subObjCfg and
v.getDefinition().(DefinitionNode).getValue() = selfDictCfg
) )
) and ) and
a.getATarget() = sub and a.getATarget() = sub and
@@ -62,9 +64,10 @@ class CheckClass extends ClassObject {
pragma[nomagic] pragma[nomagic]
private predicate monkeyPatched(string name) { private predicate monkeyPatched(string name) {
exists(Attribute a | exists(Attribute a, ControlFlowNode objCfg |
objCfg.getNode() = a.getObject() and
a.getCtx() instanceof Store and a.getCtx() instanceof Store and
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and PointsTo::points_to(objCfg, _, this, _, _) and
a.getName() = name a.getName() = name
) )
} }
@@ -84,9 +87,9 @@ class CheckClass extends ClassObject {
} }
predicate interestingUndefined(SelfAttributeRead a) { predicate interestingUndefined(SelfAttributeRead a) {
exists(string name | name = a.getName() | exists(string name, ControlFlowNode aCfg | name = a.getName() and aCfg.getNode() = a |
this.interestingContext(a, name) and this.interestingContext(a, name) and
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name) not this.definedInBlock(aCfg.getBasicBlock(), name)
) )
} }
@@ -109,8 +112,9 @@ class CheckClass extends ClassObject {
pragma[nomagic] pragma[nomagic]
private predicate definitionInBlock(BasicBlock b, string name) { private predicate definitionInBlock(BasicBlock b, string name) {
exists(SelfAttributeStore sa | exists(SelfAttributeStore sa, ControlFlowNode saCfg |
sa.getAFlowNode().getBasicBlock() = b and saCfg.getNode() = sa and
saCfg.getBasicBlock() = b and
sa.getName() = name and sa.getName() = name and
sa.getClass() = this.getPyClass() sa.getClass() = this.getPyClass()
) )

View File

@@ -15,7 +15,9 @@
import python import python
import semmle.python.ApiGraphs import semmle.python.ApiGraphs
predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() } predicate doesnt_reraise(ExceptStmt ex) {
exists(ControlFlowNode exCfg | exCfg.getNode() = ex | exCfg.getBasicBlock().reachesExit())
}
predicate catches_base_exception(ExceptStmt ex) { predicate catches_base_exception(ExceptStmt ex) {
ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr() ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr()

View File

@@ -116,7 +116,7 @@ FunctionValue get_function_or_initializer(Value func_or_cls) {
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) { predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
not func.isC() and not func.isC() and
name = call.getANamedArgumentName() and name = call.getANamedArgumentName() and
call.getAFlowNode() = get_a_call_objectapi(func) and exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call_objectapi(func)) and
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name) not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
} }
@@ -124,7 +124,7 @@ predicate illegally_named_parameter_objectapi(Call call, Object func, string nam
predicate illegally_named_parameter(Call call, Value func, string name) { predicate illegally_named_parameter(Call call, Value func, string name) {
not func.isBuiltin() and not func.isBuiltin() and
name = call.getANamedArgumentName() and name = call.getANamedArgumentName() and
call.getAFlowNode() = get_a_call(func) and exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(func)) and
not get_function_or_initializer(func).isLegalArgumentName(name) not get_function_or_initializer(func).isLegalArgumentName(name)
} }
@@ -146,7 +146,9 @@ predicate too_few_args_objectapi(Call call, Object callable, int limit) {
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
or or
callable instanceof ClassObject and callable instanceof ClassObject and
call.getAFlowNode() = get_a_call_objectapi(callable) and exists(ControlFlowNode callCfg | callCfg.getNode() = call |
callCfg = get_a_call_objectapi(callable)
) and
limit = func.minParameters() - 1 limit = func.minParameters() - 1
) )
} }
@@ -172,7 +174,7 @@ predicate too_few_args(Call call, Value callable, int limit) {
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1 call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
or or
callable instanceof ClassValue and callable instanceof ClassValue and
call.getAFlowNode() = get_a_call(callable) and exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
limit = func.minParameters() - 1 limit = func.minParameters() - 1
) )
} }
@@ -191,7 +193,9 @@ predicate too_many_args_objectapi(Call call, Object callable, int limit) {
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
or or
callable instanceof ClassObject and callable instanceof ClassObject and
call.getAFlowNode() = get_a_call_objectapi(callable) and exists(ControlFlowNode callCfg | callCfg.getNode() = call |
callCfg = get_a_call_objectapi(callable)
) and
limit = func.maxParameters() - 1 limit = func.maxParameters() - 1
) and ) and
positional_arg_count_for_call_objectapi(call, callable) > limit positional_arg_count_for_call_objectapi(call, callable) > limit
@@ -211,7 +215,7 @@ predicate too_many_args(Call call, Value callable, int limit) {
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1 call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
or or
callable instanceof ClassValue and callable instanceof ClassValue and
call.getAFlowNode() = get_a_call(callable) and exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
limit = func.maxParameters() - 1 limit = func.maxParameters() - 1
) and ) and
positional_arg_count_for_call(call, callable) > limit positional_arg_count_for_call(call, callable) > limit

View File

@@ -36,11 +36,15 @@ where
exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
( (
exists(BasicBlock b, int i1, int i2 | exists(BasicBlock b, int i1, int i2 |
k1.getAFlowNode() = b.getNode(i1) and b.getNode(i1).getNode() = k1 and
k2.getAFlowNode() = b.getNode(i2) and b.getNode(i2).getNode() = k2 and
i1 < i2 i1 < i2
) )
or or
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock()) exists(ControlFlowNode k1Cfg, ControlFlowNode k2Cfg |
k1Cfg.getNode() = k1 and k2Cfg.getNode() = k2
|
k1Cfg.getBasicBlock().strictlyDominates(k2Cfg.getBasicBlock())
)
) )
select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten" select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten"

View File

@@ -98,16 +98,18 @@ private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int en
} }
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) { private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
exists(CallNode call | call = format_expr.getAFlowNode() | exists(CallNode call, ControlFlowNode fmtCfg |
call.getNode() = format_expr and fmtCfg.getNode() = fmt
|
call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::named("format")) and call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::named("format")) and
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmt.getAFlowNode()) and call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmtCfg) and
args = count(format_expr.getAnArg()) - 1 args = count(format_expr.getAnArg()) - 1
or or
call.getFunction() call.getFunction()
.(AttrNode) .(AttrNode)
.getObject("format") .getObject("format")
.(ControlFlowNodeWithPointsTo) .(ControlFlowNodeWithPointsTo)
.pointsTo(_, fmt.getAFlowNode()) and .pointsTo(_, fmtCfg) and
args = count(format_expr.getAnArg()) args = count(format_expr.getAnArg())
) )
} }

View File

@@ -15,7 +15,7 @@ import python
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ /** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | exists(CompareNode fcomp | fcomp.getNode() = comp |
fcomp.operands(left, op, right) and fcomp.operands(left, op, right) and
(op instanceof Is or op instanceof IsNot) (op instanceof Is or op instanceof IsNot)
) )

View File

@@ -5,7 +5,7 @@ private import LegacyPointsTo
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */ /** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) { predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() | exists(CompareNode fcomp | fcomp.getNode() = comp |
fcomp.operands(left, op, right) and fcomp.operands(left, op, right) and
(op instanceof Is or op instanceof IsNot) (op instanceof Is or op instanceof IsNot)
) )

View File

@@ -19,7 +19,7 @@ where
// Only relevant for Python 2, as all later versions implement true division // Only relevant for Python 2, as all later versions implement true division
major_version() = 2 and major_version() = 2 and
exists(BinaryExprNode bin, Value lval, Value rval | exists(BinaryExprNode bin, Value lval, Value rval |
bin = div.getAFlowNode() and bin.getNode() = div and
bin.getNode().getOp() instanceof Div and bin.getNode().getOp() instanceof Div and
bin.getLeft().(ControlFlowNodeWithPointsTo).pointsTo(lval, left) and bin.getLeft().(ControlFlowNodeWithPointsTo).pointsTo(lval, left) and
lval.getClass() = ClassValue::int_() and lval.getClass() = ClassValue::int_() and

View File

@@ -19,7 +19,9 @@ where
exists(Function init | init.isInitMethod() and r.getScope() = init) and exists(Function init | init.isInitMethod() and r.getScope() = init) and
r.getValue() = rv and r.getValue() = rv and
not rv.pointsTo(Value::none_()) and not rv.pointsTo(Value::none_()) and
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and not exists(FunctionValue f, ControlFlowNode rvCfg | rvCfg.getNode() = rv |
f.getACall() = rvCfg and f.neverReturns()
) and
// to avoid double reporting, don't trigger if returning result from other __init__ function // to avoid double reporting, don't trigger if returning result from other __init__ function
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__") not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
select r, "Explicit return in __init__ method." select r, "Explicit return in __init__ method."

View File

@@ -69,7 +69,12 @@ where
returns_meaningful_value(callee) and returns_meaningful_value(callee) and
not wrapped_in_try_except(call) and not wrapped_in_try_except(call) and
exists(int unused | exists(int unused |
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and unused =
count(ExprStmt e |
exists(ControlFlowNode eValCfg | eValCfg.getNode() = e.getValue() |
eValCfg = callee.getACall()
)
) and
total = count(callee.getACall()) total = count(callee.getACall())
| |
percentage_used = (100.0 * (total - unused) / total).floor() percentage_used = (100.0 * (total - unused) / total).floor()

View File

@@ -138,12 +138,12 @@ predicate function_opens_file(FunctionValue f) {
f = Value::named("open") f = Value::named("open")
or or
exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() | exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() |
ret.getValue().getAFlowNode() = v.getAUse() and v.getNode() = ret.getValue().getAUse() and
var_is_open(v, _) var_is_open(v, _)
) )
or or
exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() | exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() |
ret.getValue().getAFlowNode() = callee.getACall() and callee.getNode() = ret.getValue().getACall() and
function_opens_file(callee) function_opens_file(callee)
) )
} }

View File

@@ -94,7 +94,7 @@ class CredentialSink extends DataFlow::Node {
this.(DataFlow::ArgumentNode).argumentOf(_, pos) this.(DataFlow::ArgumentNode).argumentOf(_, pos)
) )
or or
exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this.asCfgNode()) exists(Keyword k | k.getArg() = name and this.asCfgNode().getNode() = k.getValue())
or or
exists(CompareNode cmp, NameNode n | n.getId() = name | exists(CompareNode cmp, NameNode n | n.getId() = name |
cmp.operands(this.asCfgNode(), any(Eq eq), n) cmp.operands(this.asCfgNode(), any(Eq eq), n)

View File

@@ -25,7 +25,7 @@ from
For loop, ControlFlowNodeWithPointsTo iter, Value str, Value seq, ControlFlowNode seq_origin, For loop, ControlFlowNodeWithPointsTo iter, Value str, Value seq, ControlFlowNode seq_origin,
ControlFlowNode str_origin ControlFlowNode str_origin
where where
loop.getIter().getAFlowNode() = iter and iter.getNode() = loop.getIter() and
iter.pointsTo(str, str_origin) and iter.pointsTo(str, str_origin) and
iter.pointsTo(seq, seq_origin) and iter.pointsTo(seq, seq_origin) and
has_string_type(str) and has_string_type(str) and

View File

@@ -15,7 +15,7 @@
import python import python
predicate loop_variable_ssa(For f, Variable v, SsaVariable s) { predicate loop_variable_ssa(For f, Variable v, SsaVariable s) {
f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable() s.getDefinition().getNode() = f.getTarget() and v = s.getVariable()
} }
predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) { predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) {

View File

@@ -16,7 +16,7 @@ private import LegacyPointsTo
from For loop, ControlFlowNodeWithPointsTo iter, Value v, ClassValue t, ControlFlowNode origin from For loop, ControlFlowNodeWithPointsTo iter, Value v, ClassValue t, ControlFlowNode origin
where where
loop.getIter().getAFlowNode() = iter and iter.getNode() = loop.getIter() and
iter.pointsTo(_, v, origin) and iter.pointsTo(_, v, origin) and
v.getClass() = t and v.getClass() = t and
not t.isIterable() and not t.isIterable() and

View File

@@ -24,11 +24,13 @@ predicate func_with_side_effects(Expr e) {
} }
predicate call_with_side_effect(Call e) { predicate call_with_side_effect(Call e) {
e.getAFlowNode() = exists(ControlFlowNode eCfg | eCfg.getNode() = e |
API::moduleImport("subprocess") eCfg =
.getMember(["call", "check_call", "check_output"]) API::moduleImport("subprocess")
.getACall() .getMember(["call", "check_call", "check_output"])
.asCfgNode() .getACall()
.asCfgNode()
)
} }
predicate probable_side_effect(Expr e) { predicate probable_side_effect(Expr e) {

View File

@@ -133,7 +133,11 @@ class ListComprehensionDeclaration extends ListComp {
major_version() = 2 and major_version() = 2 and
this.getIterationVariable(_).getId() = result.getId() and this.getIterationVariable(_).getId() = result.getId() and
result.getScope() = this.getScope() and result.getScope() = this.getScope() and
this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and exists(ControlFlowNode thisCfg, ControlFlowNode resultCfg |
thisCfg.getNode() = this and resultCfg.getNode() = result
|
thisCfg.strictlyReaches(resultCfg)
) and
result.isUse() result.isUse()
} }

View File

@@ -13,18 +13,21 @@
import python import python
import Definition import Definition
from ListComprehensionDeclaration l, Name use, Name defn from
ListComprehensionDeclaration l, Name use, Name defn, ControlFlowNode lCfg, ControlFlowNode useCfg
where where
use = l.getALeakedVariableUse() and use = l.getALeakedVariableUse() and
defn = l.getDefinition() and defn = l.getDefinition() and
l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and lCfg.getNode() = l and
useCfg.getNode() = use and
lCfg.strictlyReaches(useCfg) and
/* Make sure we aren't in a loop, as the variable may be redefined */ /* Make sure we aren't in a loop, as the variable may be redefined */
not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and not useCfg.strictlyReaches(lCfg) and
not l.contains(use) and not l.contains(use) and
not use.deletes(_) and not use.deletes(_) and
not exists(SsaVariable v | not exists(SsaVariable v |
v.getAUse() = use.getAFlowNode() and v.getAUse() = useCfg and
not v.getDefinition().strictlyDominates(l.getAFlowNode()) not v.getDefinition().strictlyDominates(lCfg)
) )
select use, select use,
use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn, use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn,

View File

@@ -26,8 +26,11 @@ private Stmt loop_probably_defines(Variable v) {
/** Holds if the variable used by `use` is probably defined in a loop */ /** Holds if the variable used by `use` is probably defined in a loop */
predicate probably_defined_in_loop(Name use) { predicate probably_defined_in_loop(Name use) {
exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) | exists(Stmt loop, ControlFlowNode loopCfg, ControlFlowNode useCfg |
loop.getAFlowNode().strictlyReaches(use.getAFlowNode()) loop = loop_probably_defines(use.getVariable()) and
loopCfg.getNode() = loop and
useCfg.getNode() = use and
loopCfg.strictlyReaches(useCfg)
) )
} }

View File

@@ -24,8 +24,8 @@ predicate multiply_defined(AstNode asgn1, AstNode asgn2, Variable v) {
forex(Definition def, Definition redef | forex(Definition def, Definition redef |
def.getVariable() = v and def.getVariable() = v and
def = asgn1.getAFlowNode() and def.getNode() = asgn1 and
redef = asgn2.getAFlowNode() redef.getNode() = asgn2
| |
def.isUnused() and def.isUnused() and
def.getARedef() = redef and def.getARedef() = redef and

View File

@@ -88,7 +88,9 @@ predicate implicit_repeat(For f) {
* E.g. gets `x` from `{ y for y in x }`. * E.g. gets `x` from `{ y for y in x }`.
*/ */
ControlFlowNode get_comp_iterable(For f) { ControlFlowNode get_comp_iterable(For f) {
exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result) exists(Comp c, ControlFlowNode cCfg |
c.getFunction().getStmt(0) = f and cCfg.getNode() = c and cCfg.getAPredecessor() = result
)
} }
from For f, Variable v, string msg from For f, Variable v, string msg

View File

@@ -19,9 +19,10 @@ private predicate loop_entry_variables(EssaVariable pred, EssaVariable succ) {
private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) { private predicate loop_entry_edge(BasicBlock pred, BasicBlock loop) {
pred = loop.getAPredecessor() and pred = loop.getAPredecessor() and
pred = loop.getImmediateDominator() and pred = loop.getImmediateDominator() and
exists(Stmt s | exists(Stmt s, ControlFlowNode sCfg |
loop_probably_executes_at_least_once(s) and loop_probably_executes_at_least_once(s) and
s.getAFlowNode().getBasicBlock() = loop sCfg.getNode() = s and
sCfg.getBasicBlock() = loop
) )
} }

View File

@@ -27,7 +27,7 @@ predicate guarded_against_name_error(Name u) {
| |
globals.getFunc().(Name).getId() = "globals" and globals.getFunc().(Name).getId() = "globals" and
guard.controls(controlled, _) and guard.controls(controlled, _) and
controlled.contains(u.getAFlowNode()) exists(ControlFlowNode uCfg | uCfg.getNode() = u | controlled.contains(uCfg))
) )
} }
@@ -101,18 +101,18 @@ predicate undefined_use(Name u) {
} }
private predicate first_use_in_a_block(Name use) { private predicate first_use_in_a_block(Name use) {
exists(GlobalVariable v, BasicBlock b, int i | exists(GlobalVariable v, BasicBlock b, int i, ControlFlowNode useCfg | useCfg.getNode() = use |
i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode() i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = useCfg
) )
} }
predicate first_undefined_use(Name use) { predicate first_undefined_use(Name use) {
undefined_use(use) and undefined_use(use) and
exists(GlobalVariable v | v.getALoad() = use | exists(GlobalVariable v, ControlFlowNode useCfg | v.getALoad() = use and useCfg.getNode() = use |
first_use_in_a_block(use) and first_use_in_a_block(use) and
not exists(ControlFlowNode other | not exists(ControlFlowNode other |
other.getNode() = v.getALoad() and other.getNode() = v.getALoad() and
other.getBasicBlock().strictlyDominates(use.getAFlowNode().getBasicBlock()) other.getBasicBlock().strictlyDominates(useCfg.getBasicBlock())
) )
) )
} }

View File

@@ -18,8 +18,8 @@ private import semmle.python.types.ImportTime
/* Local variable part */ /* Local variable part */
predicate initialized_as_local(PlaceHolder use) { predicate initialized_as_local(PlaceHolder use) {
exists(SsaVariableWithPointsTo l, Function f | exists(SsaVariableWithPointsTo l, Function f, ControlFlowNode useCfg |
f = use.getScope() and l.getAUse() = use.getAFlowNode() f = use.getScope() and useCfg.getNode() = use and l.getAUse() = useCfg
| |
l.getVariable() instanceof LocalVariable and l.getVariable() instanceof LocalVariable and
not l.maybeUndefined() not l.maybeUndefined()

View File

@@ -54,7 +54,7 @@ predicate unused_global(Name unused, GlobalVariable v) {
u.uses(v) u.uses(v)
| |
// That is reachable from this definition, directly // That is reachable from this definition, directly
defn.strictlyReaches(u.getAFlowNode()) exists(ControlFlowNode uCfg | uCfg.getNode() = u | defn.strictlyReaches(uCfg))
or or
// indirectly // indirectly
defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope()

View File

@@ -48,15 +48,17 @@ class Symbol extends TSymbol {
AstNode find() { AstNode find() {
this = TModule(result) this = TModule(result)
or or
exists(Symbol s, string name | this = TMember(s, name) | exists(Symbol s, string name, ControlFlowNode resultCfg |
this = TMember(s, name) and resultCfg.getNode() = result
|
exists(ClassObject cls | exists(ClassObject cls |
s.resolvesTo() = cls and s.resolvesTo() = cls and
cls.attributeRefersTo(name, _, result.getAFlowNode()) cls.attributeRefersTo(name, _, resultCfg)
) )
or or
exists(ModuleObject m | exists(ModuleObject m |
s.resolvesTo() = m and s.resolvesTo() = m and
m.attributeRefersTo(name, _, result.getAFlowNode()) m.attributeRefersTo(name, _, resultCfg)
) )
) )
} }

View File

@@ -80,10 +80,11 @@ class VersionGuard extends ConditionBlock {
VersionGuard() { this.getLastNode() instanceof VersionTest } VersionGuard() { this.getLastNode() instanceof VersionTest }
} }
from ImportExpr ie from ImportExpr ie, ControlFlowNode ieCfg
where where
ieCfg.getNode() = ie and
not ie.(ExprWithPointsTo).refersTo(_) and not ie.(ExprWithPointsTo).refersTo(_) and
exists(Context c | c.appliesTo(ie.getAFlowNode())) and exists(Context c | c.appliesTo(ieCfg)) and
not ok_to_fail(ie) and not ok_to_fail(ie) and
not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().getBasicBlock(), _)) not exists(VersionGuard guard | guard.controls(ieCfg.getBasicBlock(), _))
select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'." select ie, "Unable to resolve import of '" + ie.getImportedModuleName() + "'."

View File

@@ -11,13 +11,13 @@ import python
import semmle.python.pointsto.PointsTo import semmle.python.pointsto.PointsTo
predicate points_to_failure(Expr e) { predicate points_to_failure(Expr e) {
exists(ControlFlowNode f | f = e.getAFlowNode() | not PointsTo::pointsTo(f, _, _, _)) exists(ControlFlowNode f | f.getNode() = e | not PointsTo::pointsTo(f, _, _, _))
} }
predicate key_points_to_failure(Expr e) { predicate key_points_to_failure(Expr e) {
points_to_failure(e) and points_to_failure(e) and
not points_to_failure(e.getASubExpression()) and not points_to_failure(e.getASubExpression()) and
not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() | not exists(SsaVariable ssa, ControlFlowNode eCfg | eCfg.getNode() = e and ssa.getAUse() = eCfg |
points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode()) points_to_failure(ssa.getAnUltimateDefinition().getDefinition().getNode())
) and ) and
not exists(Assign a | a.getATarget() = e) not exists(Assign a | a.getATarget() = e)

View File

@@ -12,5 +12,5 @@ import python
private import LegacyPointsTo private import LegacyPointsTo
from Expr e from Expr e
where exists(ControlFlowNodeWithPointsTo f | f = e.getAFlowNode() | not f.refersTo(_)) where exists(ControlFlowNodeWithPointsTo f | f.getNode() = e | not f.refersTo(_))
select e, "Expression does not 'point-to' any object." select e, "Expression does not 'point-to' any object."

View File

@@ -131,7 +131,7 @@ module ModificationOfParameterWithDefault {
exists(DeletionNode d | d.getTarget().(SubscriptNode).getObject() = this.asCfgNode()) exists(DeletionNode d | d.getTarget().(SubscriptNode).getObject() = this.asCfgNode())
or or
// augmented assignment to the value // augmented assignment to the value
exists(AugAssign a | a.getTarget().getAFlowNode() = this.asCfgNode()) exists(AugAssign a | this.asCfgNode().getNode() = a.getTarget())
or or
// modifying function call // modifying function call
exists(DataFlow::CallCfgNode c, DataFlow::AttrRead a | c.getFunction() = a | exists(DataFlow::CallCfgNode c, DataFlow::AttrRead a | c.getFunction() = a |

View File

@@ -5,5 +5,7 @@
import python import python
select count(Comprehension c | select count(Comprehension c |
count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode()) count(c.toString()) != 1 or
count(c.getLocation()) != 1 or
not exists(ControlFlowNode n | n.getNode() = c)
) )

View File

@@ -45,13 +45,15 @@ private class VersionGuardedNode extends DataFlow::Node {
VersionGuardedNode() { VersionGuardedNode() {
version in [2, 3] and version in [2, 3] and
exists(If parent, CompareNode c | parent.getBody().contains(this.asExpr()) | exists(If parent, CompareNode c, ControlFlowNode litCfg |
parent.getBody().contains(this.asExpr()) and
litCfg.getNode() = any(IntegerLiteral lit | lit.getValue() = version)
|
c.operands(API::moduleImport("sys") c.operands(API::moduleImport("sys")
.getMember("version_info") .getMember("version_info")
.getASubscript() .getASubscript()
.asSource() .asSource()
.asCfgNode(), any(Eq eq), .asCfgNode(), any(Eq eq), litCfg)
any(IntegerLiteral lit | lit.getValue() = version).getAFlowNode())
) )
} }

View File

@@ -9,7 +9,7 @@ Expr assignedValue(Name n) {
from Name def, DefinitionNode d from Name def, DefinitionNode d
where where
d = def.getAFlowNode() and d.getNode() = def and
exists(assignedValue(def)) and exists(assignedValue(def)) and
not d.getValue().getNode() = assignedValue(def) not d.getValue().getNode() = assignedValue(def)
select def.toString(), assignedValue(def) select def.toString(), assignedValue(def)

View File

@@ -8,4 +8,4 @@ where
not a instanceof ExprStmt and not a instanceof ExprStmt and
a.getScope() = s and a.getScope() = s and
s instanceof Function s instanceof Function
select a.getLocation().getStartLine(), s.getName(), a, count(a.getAFlowNode()) select a.getLocation().getStartLine(), s.getName(), a, count(ControlFlowNode n | n.getNode() = a)

View File

@@ -3,6 +3,6 @@ private import LegacyPointsTo
from ControlFlowNode f, PointsToContext ctx, Value obj, ControlFlowNode orig from ControlFlowNode f, PointsToContext ctx, Value obj, ControlFlowNode orig
where where
exists(ExprStmt s | s.getValue().getAFlowNode() = f) and exists(ExprStmt s | f.getNode() = s.getValue()) and
PointsTo::pointsTo(f, ctx, obj, orig) PointsTo::pointsTo(f, ctx, obj, orig)
select ctx, f, obj.toString(), orig select ctx, f, obj.toString(), orig

View File

@@ -4,6 +4,6 @@ import semmle.python.objects.ObjectInternal
from ControlFlowNode f, ObjectInternal obj, ControlFlowNode orig from ControlFlowNode f, ObjectInternal obj, ControlFlowNode orig
where where
exists(ExprStmt s | s.getValue().getAFlowNode() = f) and exists(ExprStmt s | f.getNode() = s.getValue()) and
PointsTo::pointsTo(f, _, obj, orig) PointsTo::pointsTo(f, _, obj, orig)
select f, obj.toString(), orig select f, obj.toString(), orig

View File

@@ -43,7 +43,7 @@ query predicate test_taint(string arg_location, string test_res, string scope_na
// TODO: Replace with `hasFlowToExpr` once that is working // TODO: Replace with `hasFlowToExpr` once that is working
if if
TestTaintTrackingFlow::flowTo(any(DataFlow::Node n | TestTaintTrackingFlow::flowTo(any(DataFlow::Node n |
n.(DataFlow::CfgNode).getNode() = arg.getAFlowNode() n.(DataFlow::CfgNode).getNode().getNode() = arg
)) ))
then has_taint = true then has_taint = true
else has_taint = false else has_taint = false

View File

@@ -238,9 +238,10 @@ module PointsToBasedCallGraph {
Value calleeValue; Value calleeValue;
ResolvableRecordedCall() { ResolvableRecordedCall() {
exists(Call call, XmlCallee xmlCallee | exists(Call call, XmlCallee xmlCallee, ControlFlowNode callCfg |
call = this.getACall() and call = this.getACall() and
calleeValue.getACall() = call.getAFlowNode() and callCfg.getNode() = call and
calleeValue.getACall() = callCfg and
xmlCallee = this.getXmlCallee() and xmlCallee = this.getXmlCallee() and
( (
xmlCallee instanceof XmlPythonCallee and xmlCallee instanceof XmlPythonCallee and

View File

@@ -0,0 +1,12 @@
| test.rs:19:9:19:34 | ...::compute(...) | HashingAlgorithm MD5 WEAK inputs:1 |
| test.rs:20:9:20:40 | ...::compute(...) | HashingAlgorithm MD5 WEAK inputs:1 |
| test.rs:21:9:21:34 | ...::compute(...) | HashingAlgorithm MD5 WEAK inputs:1 |
| test.rs:22:9:22:44 | ...::compute(...) | HashingAlgorithm MD5 WEAK inputs:1 |
| test.rs:67:26:67:40 | ...::new(...) | HashingAlgorithm MD5 WEAK |
| test.rs:73:9:73:23 | ...::new(...) | HashingAlgorithm MD5 WEAK |
| test.rs:74:9:74:23 | ...::new(...) | HashingAlgorithm MD5 WEAK |
| test.rs:133:26:133:40 | ...::new(...) | HashingAlgorithm MD5 WEAK |
| test.rs:156:26:156:40 | ...::new(...) | HashingAlgorithm MD5 WEAK |
| test.rs:176:13:176:24 | ...::new(...) | EncryptionAlgorithm SEED |
| test.rs:199:22:199:32 | ...::new(...) | HashingAlgorithm SHA1 WEAK |
| test.rs:211:13:211:35 | ...::compute(...) | HashingAlgorithm MD5 WEAK inputs:1 |

View File

@@ -0,0 +1,3 @@
query: queries/summary/CryptographicOperations.ql
postprocess:
- utils/test/InlineExpectationsTestQuery.ql

View File

@@ -1,9 +1,13 @@
#select #select
| test.rs:20:9:20:24 | ...::compute | test.rs:20:26:20:39 | credit_card_no | test.rs:20:9:20:24 | ...::compute | $@ is used in a hashing algorithm (MD5) that is insecure. | test.rs:20:26:20:39 | credit_card_no | Sensitive data (private) | | test.rs:20:9:20:24 | ...::compute | test.rs:20:26:20:39 | credit_card_no | test.rs:20:9:20:24 | ...::compute | $@ is used in a hashing algorithm (MD5) that is insecure. | test.rs:20:26:20:39 | credit_card_no | Sensitive data (private) |
| test.rs:21:9:21:24 | ...::compute | test.rs:21:26:21:33 | password | test.rs:21:9:21:24 | ...::compute | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test.rs:21:26:21:33 | password | Sensitive data (password) | | test.rs:21:9:21:24 | ...::compute | test.rs:21:26:21:33 | password | test.rs:21:9:21:24 | ...::compute | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test.rs:21:26:21:33 | password | Sensitive data (password) |
| test.rs:211:13:211:28 | ...::compute | test.rs:226:29:226:36 | password | test.rs:211:13:211:28 | ...::compute | $@ is used in a hashing algorithm (MD5) that is insecure for password hashing, since it is not a computationally expensive hash function. | test.rs:226:29:226:36 | password | Sensitive data (password) |
edges edges
| test.rs:20:26:20:39 | credit_card_no | test.rs:20:9:20:24 | ...::compute | provenance | MaD:1 Sink:MaD:1 | | test.rs:20:26:20:39 | credit_card_no | test.rs:20:9:20:24 | ...::compute | provenance | MaD:1 Sink:MaD:1 |
| test.rs:21:26:21:33 | password | test.rs:21:9:21:24 | ...::compute | provenance | MaD:1 Sink:MaD:1 | | test.rs:21:26:21:33 | password | test.rs:21:9:21:24 | ...::compute | provenance | MaD:1 Sink:MaD:1 |
| test.rs:210:20:210:30 | ...: ... | test.rs:211:30:211:34 | value | provenance | |
| test.rs:211:30:211:34 | value | test.rs:211:13:211:28 | ...::compute | provenance | MaD:1 Sink:MaD:1 |
| test.rs:226:29:226:36 | password | test.rs:210:20:210:30 | ...: ... | provenance | |
models models
| 1 | Sink: md5::compute; Argument[0]; hasher-input | | 1 | Sink: md5::compute; Argument[0]; hasher-input |
nodes nodes
@@ -11,4 +15,8 @@ nodes
| test.rs:20:26:20:39 | credit_card_no | semmle.label | credit_card_no | | test.rs:20:26:20:39 | credit_card_no | semmle.label | credit_card_no |
| test.rs:21:9:21:24 | ...::compute | semmle.label | ...::compute | | test.rs:21:9:21:24 | ...::compute | semmle.label | ...::compute |
| test.rs:21:26:21:33 | password | semmle.label | password | | test.rs:21:26:21:33 | password | semmle.label | password |
| test.rs:210:20:210:30 | ...: ... | semmle.label | ...: ... |
| test.rs:211:13:211:28 | ...::compute | semmle.label | ...::compute |
| test.rs:211:30:211:34 | value | semmle.label | value |
| test.rs:226:29:226:36 | password | semmle.label | password |
subpaths subpaths

View File

@@ -16,10 +16,10 @@ fn test_hash_algorithms(
_ = md5::Md5::digest(encrypted_password); _ = md5::Md5::digest(encrypted_password);
// MD5 (alternative / older library) // MD5 (alternative / older library)
_ = md5_alt::compute(harmless); _ = md5_alt::compute(harmless); // $ Alert[rust/summary/cryptographic-operations]
_ = md5_alt::compute(credit_card_no); // $ Alert[rust/weak-sensitive-data-hashing] _ = md5_alt::compute(credit_card_no); // $ Alert[rust/summary/cryptographic-operations] Alert[rust/weak-sensitive-data-hashing]
_ = md5_alt::compute(password); // $ Alert[rust/weak-sensitive-data-hashing] _ = md5_alt::compute(password); // $ Alert[rust/summary/cryptographic-operations] Alert[rust/weak-sensitive-data-hashing]
_ = md5_alt::compute(encrypted_password); _ = md5_alt::compute(encrypted_password); // $ Alert[rust/summary/cryptographic-operations]
// SHA-1 // SHA-1
_ = sha1::Sha1::digest(harmless); _ = sha1::Sha1::digest(harmless);
@@ -64,14 +64,14 @@ fn test_hash_code_patterns(
_ = md5::Md5::digest(password_vec); // $ MISSING: Alert[rust/weak-sensitive-data-hashing] _ = md5::Md5::digest(password_vec); // $ MISSING: Alert[rust/weak-sensitive-data-hashing]
// hash through a hasher object // hash through a hasher object
let mut md5_hasher = md5::Md5::new(); let mut md5_hasher = md5::Md5::new(); // $ Alert[rust/summary/cryptographic-operations]
md5_hasher.update(b"abc"); md5_hasher.update(b"abc");
md5_hasher.update(harmless); md5_hasher.update(harmless);
md5_hasher.update(password); // $ MISSING: Alert[rust/weak-sensitive-data-hashing] md5_hasher.update(password); // $ MISSING: Alert[rust/weak-sensitive-data-hashing]
_ = md5_hasher.finalize(); _ = md5_hasher.finalize();
_ = md5::Md5::new().chain_update(harmless).chain_update(harmless).chain_update(harmless).finalize(); _ = md5::Md5::new().chain_update(harmless).chain_update(harmless).chain_update(harmless).finalize(); // $ Alert[rust/summary/cryptographic-operations]
_ = md5::Md5::new().chain_update(harmless).chain_update(password).chain_update(harmless).finalize(); // $ MISSING: Alert[rust/weak-sensitive-data-hashing] _ = md5::Md5::new().chain_update(harmless).chain_update(password).chain_update(harmless).finalize(); // $ Alert[rust/summary/cryptographic-operations] MISSING: Alert[rust/weak-sensitive-data-hashing]
_ = md5::Md5::new_with_prefix(harmless).finalize(); _ = md5::Md5::new_with_prefix(harmless).finalize();
_ = md5::Md5::new_with_prefix(password).finalize(); // $ MISSING: Alert[rust/weak-sensitive-data-hashing] _ = md5::Md5::new_with_prefix(password).finalize(); // $ MISSING: Alert[rust/weak-sensitive-data-hashing]
@@ -130,7 +130,7 @@ fn test_hash_structs() {
let str3c = serde_urlencoded::to_string(&s3).unwrap(); let str3c = serde_urlencoded::to_string(&s3).unwrap();
// hash with MD5 // hash with MD5
let mut md5_hasher = md5::Md5::new(); let mut md5_hasher = md5::Md5::new(); // $ Alert[rust/summary/cryptographic-operations]
md5_hasher.update(s1.data); md5_hasher.update(s1.data);
md5_hasher.update(s2.credit_card_no); // $ MISSING: Alert[rust/weak-sensitive-data-hashing] md5_hasher.update(s2.credit_card_no); // $ MISSING: Alert[rust/weak-sensitive-data-hashing]
md5_hasher.update(s3.password); // $ MISSING: Alert[rust/weak-sensitive-data-hashing] md5_hasher.update(s3.password); // $ MISSING: Alert[rust/weak-sensitive-data-hashing]
@@ -153,8 +153,75 @@ fn test_hash_file(
let mut harmless_file = std::fs::File::open(harmless_filename).unwrap(); let mut harmless_file = std::fs::File::open(harmless_filename).unwrap();
let mut password_file = std::fs::File::open(password_filename).unwrap(); let mut password_file = std::fs::File::open(password_filename).unwrap();
let mut md5_hasher = md5::Md5::new(); let mut md5_hasher = md5::Md5::new(); // $ Alert[rust/summary/cryptographic-operations]
_ = std::io::copy(&mut harmless_file, &mut md5_hasher); _ = std::io::copy(&mut harmless_file, &mut md5_hasher);
_ = std::io::copy(&mut password_file, &mut md5_hasher); // $ MISSING: Alert[rust/weak-sensitive-data-hashing] _ = std::io::copy(&mut password_file, &mut md5_hasher); // $ MISSING: Alert[rust/weak-sensitive-data-hashing]
_ = md5_hasher.finalize(); _ = md5_hasher.finalize();
} }
// ---
struct Seed {
}
impl Seed {
fn new(_seed_value: u64) -> Self {
Seed { }
}
}
fn test_seed() {
// this will be misrecognized as a use of the SEED algorithm, but SEED is strong and the input
// is not sensitive data, so `rust/weak-sensitive-data-hashing` should not report a result here.
let _ = Seed::new(0); // $ Alert[rust/summary/cryptographic-operations]
}
// ---
struct Sha1 {
}
impl Sha1 {
const fn new() -> Self {
Sha1 { }
}
const fn update(&mut self, _data: &[u8]) {
// ...
}
const fn finalize(self) -> [u8; 20] {
[0; 20]
}
}
fn sha1_test(password: &[u8]) {
let mut hasher = Sha1::new(); // $ Alert[rust/summary/cryptographic-operations]
hasher.update(password); // $ MISSING: Alert[rust/weak-sensitive-data-hashing]
_ = hasher.finalize();
}
// ---
struct HashCollection {
}
impl HashCollection {
pub fn add_sig(value: &str) -> Self {
_ = md5_alt::compute(value); // $ Alert[rust/summary/cryptographic-operations] Alert[rust/weak-sensitive-data-hashing]
// ...
HashCollection { }
}
}
fn test_hash_collection() {
// this indirectly performs MD5 hashing, but the data is not sensitive
let id: &str = "my_id_1234567890";
HashCollection::add_sig(id);
// this indirectly performs MD5 hashing, and the data is sensitive; the result is reported here
let password: &str = "password123";
HashCollection::add_sig(password); // $ Source
}