Compare commits

..

2 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
f3c85c0ff7 Extend C# control flow elements with type mentions 2026-06-22 13:17:44 +00:00
copilot-swe-agent[bot]
d7ec468e2e Add type mentions to control flow element handling 2026-06-22 12:29:48 +00:00
97 changed files with 4483 additions and 5164 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
{ {
var type = Type.Create(Context, Context.GetType(Stmt.Declaration!.Type)); var type = Type.Create(Context, Context.GetType(Stmt.Declaration!.Type));
trapFile.catch_type(this, type.TypeRef, true); trapFile.catch_type(this, type.TypeRef, true);
Expression.Create(Context, Stmt.Declaration!.Type, this, 0); TypeMention.Create(Context, Stmt.Declaration!.Type, this, type);
} }
else // A catch clause of the form 'catch { ... }' else // A catch clause of the form 'catch { ... }'
{ {

View File

@@ -121,6 +121,13 @@ 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

@@ -995,23 +995,6 @@ class SpecificCatchClause extends CatchClause {
/** Gets the local variable declaration of this catch clause, if any. */ /** Gets the local variable declaration of this catch clause, if any. */
LocalVariableDeclExpr getVariableDeclExpr() { result.getParent() = this } LocalVariableDeclExpr getVariableDeclExpr() { result.getParent() = this }
/**
* Gets the type access of this catch clause, if it has no variable declaration.
*
* For example, the type access in
*
* ```csharp
* try { ... }
* catch (IOException) { ... }
* ```
*
* is `IOException`.
*/
TypeAccess getTypeAccess() {
not exists(this.getVariableDeclExpr()) and
result = this.getChild(0)
}
override string toString() { result = "catch (...) {...}" } override string toString() { result = "catch (...) {...}" }
override string getAPrimaryQlClass() { result = "SpecificCatchClause" } override string getAPrimaryQlClass() { result = "SpecificCatchClause" }

View File

@@ -6,6 +6,7 @@ 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
@@ -1286,7 +1287,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 @type_mention { class TypeMention extends ControlFlowElement, @type_mention {
Type type; Type type;
@type_mention_parent parent; @type_mention_parent parent;
@@ -1319,13 +1320,13 @@ class TypeMention extends @type_mention {
* } * }
* ``` * ```
*/ */
TypeMention getParent() { result = parent } override TypeMention getParent() { result = parent }
/** Gets a textual representation of this type mention. */ /** Gets a textual representation of this type mention. */
string toString() { result = type.toString() } override string toString() { result = type.toString() }
/** Gets the location of this type mention. */ /** Gets the location of this type mention. */
Location getLocation() { type_mention_location(this, result) } override Location getALocation() { 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() { none() } Callable getEnclosingCallable() { enclosingCallable(this, result) }
/** Gets the assembly that this element was compiled into. */ /** Gets the assembly that this element was compiled into. */
Assembly getAssembly() { Assembly getAssembly() {

View File

@@ -90,7 +90,6 @@ module Ast implements AstSig<Location> {
private AstNode getStmtChild0(Stmt s, int i) { private AstNode getStmtChild0(Stmt s, int i) {
not s instanceof FixedStmt and not s instanceof FixedStmt and
not s instanceof UsingBlockStmt and not s instanceof UsingBlockStmt and
not skipControlFlow(result) and
result = s.getChild(i) result = s.getChild(i)
or or
s = s =

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 | @externalDataElement | @using_directive | @type_parameter_constraints | @type_mention | @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; @control_flow_element = @stmt | @expr | @parameter | @type_mention;
/* XML Files */ /* XML Files */

View File

@@ -101,8 +101,7 @@ csharp6.cs:
# 32| 0: [IntLiteral] 2 # 32| 0: [IntLiteral] 2
# 32| 0: [IntLiteral] 1 # 32| 0: [IntLiteral] 1
# 34| 1: [SpecificCatchClause] catch (...) {...} # 34| 1: [SpecificCatchClause] catch (...) {...}
# 34| 0: [TypeAccess] access to type IndexOutOfRangeException # 34| 0: [TypeMention] IndexOutOfRangeException
# 34| 0: [TypeMention] IndexOutOfRangeException
# 35| 1: [BlockStmt] {...} # 35| 1: [BlockStmt] {...}
# 34| 2: [EQExpr] ... == ... # 34| 2: [EQExpr] ... == ...
# 34| 0: [PropertyCall] access to property Value # 34| 0: [PropertyCall] access to property Value

View File

@@ -13,7 +13,7 @@ func logSomething(entry *logrus.Entry) {
entry.Traceln(text) // $ logger=text entry.Traceln(text) // $ logger=text
} }
func logrusCalls(selector int) { func logrusCalls() {
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,15 +27,11 @@ func logrusCalls(selector int) {
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.Infof(fmt, text) // $ logger=fmt logger=text logrus.Fatalf(fmt, text) // $ logger=fmt logger=text
if selector == 0 { logrus.Panicln(text) // $ logger=text
logrus.Fatalf(fmt, text) // $ logger=fmt logger=text logrus.Infof(fmt, text) // $ logger=fmt logger=text
} else if selector == 1 { logrus.FatalFn(fn) // $ logger=fn
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(len(v)) stdlib()
slogTest() slogTest()
} }

View File

@@ -4,69 +4,37 @@ import (
"log" "log"
) )
func stdlib(selector int) { func stdlib() {
var logger log.Logger var logger log.Logger
logger.SetPrefix("prefix: ") logger.SetPrefix("prefix: ")
switch selector { logger.Fatal(text) // $ logger=text
case 0: logger.Fatalf(fmt, text) // $ logger=fmt logger=text
logger.Fatal(text) // $ logger=text logger.Fatalln(text) // $ logger=text
case 1: logger.Panic(text) // $ logger=text
logger.Fatalf(fmt, text) // $ logger=fmt logger=text logger.Panicf(fmt, text) // $ logger=fmt logger=text
case 2: logger.Panicln(text) // $ logger=text
logger.Fatalln(text) // $ logger=text logger.Print(text) // $ logger=text
case 3: logger.Printf(fmt, text) // $ logger=fmt logger=text
logger.Panic(text) // $ logger=text logger.Println(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
switch selector { logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 9: logger.Panicf("%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 logger.Printf("%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: ")
switch selector { log.Fatal(text) // $ logger=text
case 12: log.Fatalf(fmt, text) // $ logger=fmt logger=text
log.Fatal(text) // $ logger=text log.Fatalln(text) // $ logger=text
case 13: log.Panic(text) // $ logger=text
log.Fatalf(fmt, text) // $ logger=fmt logger=text log.Panicf(fmt, text) // $ logger=fmt logger=text
case 14: log.Panicln(text) // $ logger=text
log.Fatalln(text) // $ logger=text log.Print(text) // $ logger=text
case 15: log.Printf(fmt, text) // $ logger=fmt logger=text
log.Panic(text) // $ logger=text log.Println(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
switch selector { log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
case 21: log.Panicf("%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 log.Printf("%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,265 +34,6 @@
| 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,4 +1,3 @@
| 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

@@ -1,118 +0,0 @@
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,11 +213,9 @@ 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) {
exists(ControlFlowNode this_, ControlFlowNode origin_ | this.getAFlowNode()
this_.getNode() = this and origin_.getNode() = origin .(ControlFlowNodeWithPointsTo)
| .refersTo(context, obj, cls, origin.getAFlowNode())
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
)
} }
/** /**
@@ -228,11 +226,7 @@ class ExprWithPointsTo extends Expr {
*/ */
pragma[nomagic] pragma[nomagic]
predicate refersTo(Object obj, AstNode origin) { predicate refersTo(Object obj, AstNode origin) {
exists(ControlFlowNode this_, ControlFlowNode origin_ | this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
)
} }
/** /**
@@ -246,22 +240,16 @@ 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) {
exists(ControlFlowNode this_, ControlFlowNode origin_ | this.getAFlowNode()
this_.getNode() = this and origin_.getNode() = origin .(ControlFlowNodeWithPointsTo)
| .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) {
exists(ControlFlowNode this_, ControlFlowNode origin_ | this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
this_.getNode() = this and origin_.getNode() = origin
|
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
)
} }
/** /**
@@ -487,10 +475,7 @@ 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 |
exists(CallNode call_ | call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
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
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def)) jump_to_defn(use.getAFlowNode(), 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) {
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and jump_to_defn(use.getAFlowNode(), 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

@@ -1,5 +0,0 @@
---
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

@@ -1,4 +0,0 @@
---
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,17 +17,12 @@ 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
deprecated ControlFlowNode getAFlowNode() { 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,9 +28,7 @@ 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 */
exists(ControlFlowNode n | n.getNode() = this | this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt
n.getASuccessor().getNode() instanceof ExceptStmt
)
or or
this.getASubExpression().hasSideEffects() this.getASubExpression().hasSideEffects()
} }
@@ -70,7 +68,7 @@ class Attribute extends Attribute_ {
/* syntax: Expr.name */ /* syntax: Expr.name */
override Expr getASubExpression() { result = this.getObject() } override Expr getASubExpression() { result = this.getObject() }
deprecated override AttrNode getAFlowNode() { result = super.getAFlowNode() } 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() }
@@ -99,7 +97,7 @@ class Subscript extends Subscript_ {
Expr getObject() { result = Subscript_.super.getValue() } Expr getObject() { result = Subscript_.super.getValue() }
deprecated override SubscriptNode getAFlowNode() { result = super.getAFlowNode() } override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** A call expression, such as `func(...)` */ /** A call expression, such as `func(...)` */
@@ -115,7 +113,7 @@ class Call extends Call_ {
override string toString() { result = this.getFunc().toString() + "()" } override string toString() { result = this.getFunc().toString() + "()" }
deprecated override CallNode getAFlowNode() { result = super.getAFlowNode() } 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() }
@@ -203,7 +201,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()
} }
deprecated override IfExprNode getAFlowNode() { result = super.getAFlowNode() } 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` */
@@ -413,7 +411,7 @@ class PlaceHolder extends PlaceHolder_ {
override string toString() { result = "$" + this.getId() } override string toString() { result = "$" + this.getId() }
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() } 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 )` */
@@ -480,7 +478,7 @@ class Name extends Name_ {
override string toString() { result = this.getId() } override string toString() { result = this.getId() }
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() } 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 "." */
@@ -587,7 +585,7 @@ abstract class NameConstant extends Name, ImmutableLiteral {
override predicate isConstant() { any() } override predicate isConstant() { any() }
deprecated override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } 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 as Py import python
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(Py::Expr load_store | exists(Py::AugAssign aa | aa.getTarget() = load_store) | exists(Expr load_store | exists(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 Py::AstNode toAst(ControlFlowNode n) { py_flow_bb_node(n, result, _, _) } private 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 Py::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(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this)) exists(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(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 5, e) or augstore(_, this)) exists(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(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 2, e)) } predicate isDelete() { exists(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(Py::Expr e | e = toAst(this) | py_expr_contexts(_, 4, e)) } predicate isParameter() { exists(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 Py::Bytes toAst(this) instanceof Bytes
or or
toAst(this) instanceof Py::Dict toAst(this) instanceof Dict
or or
toAst(this) instanceof Py::DictComp toAst(this) instanceof DictComp
or or
toAst(this) instanceof Py::Set toAst(this) instanceof Set
or or
toAst(this) instanceof Py::SetComp toAst(this) instanceof SetComp
or or
toAst(this) instanceof Py::Ellipsis toAst(this) instanceof Ellipsis
or or
toAst(this) instanceof Py::GeneratorExp toAst(this) instanceof GeneratorExp
or or
toAst(this) instanceof Py::Lambda toAst(this) instanceof Lambda
or or
toAst(this) instanceof Py::ListComp toAst(this) instanceof ListComp
or or
toAst(this) instanceof Py::List toAst(this) instanceof List
or or
toAst(this) instanceof Py::Num toAst(this) instanceof Num
or or
toAst(this) instanceof Py::Tuple toAst(this) instanceof Tuple
or or
toAst(this) instanceof Py::Unicode toAst(this) instanceof Unicode
or or
toAst(this) instanceof Py::NameConstant toAst(this) instanceof NameConstant
} }
/** Whether this flow node corresponds to an attribute expression */ /** Whether this flow node corresponds to an attribute expression */
predicate isAttribute() { toAst(this) instanceof Py::Attribute } predicate isAttribute() { toAst(this) instanceof Attribute }
/** Whether this flow node corresponds to an subscript expression */ /** Whether this flow node corresponds to an subscript expression */
predicate isSubscript() { toAst(this) instanceof Py::Subscript } predicate isSubscript() { toAst(this) instanceof Subscript }
/** Whether this flow node corresponds to an import member */ /** Whether this flow node corresponds to an import member */
predicate isImportMember() { toAst(this) instanceof Py::ImportMember } predicate isImportMember() { toAst(this) instanceof ImportMember }
/** Whether this flow node corresponds to a call */ /** Whether this flow node corresponds to a call */
predicate isCall() { toAst(this) instanceof Py::Call } predicate isCall() { toAst(this) instanceof 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 Py::Module } predicate isModuleEntry() { this.isEntryNode() and toAst(this) instanceof Module }
/** Whether this flow node corresponds to an import */ /** Whether this flow node corresponds to an import */
predicate isImport() { toAst(this) instanceof Py::ImportExpr } predicate isImport() { toAst(this) instanceof ImportExpr }
/** Whether this flow node corresponds to a conditional expression */ /** Whether this flow node corresponds to a conditional expression */
predicate isIfExp() { toAst(this) instanceof Py::IfExp } predicate isIfExp() { toAst(this) instanceof 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 Py::FunctionExpr } predicate isFunction() { toAst(this) instanceof 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 Py::ClassExpr } predicate isClass() { toAst(this) instanceof 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 */
Py::AstNode getNode() { py_flow_bb_node(this, result, _, _) } 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(Py::Scope s | s.getEntryNode() = this | exists(Scope s | s.getEntryNode() = this |
result = "Entry node for " + concat( | | s.toString(), ",") result = "Entry node for " + concat( | | s.toString(), ",")
) )
or or
exists(Py::Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString()) exists(Scope s | s.getANormalExit() = this | result = "Exit node for " + s.toString())
or or
not exists(Py::Scope s | s.getEntryNode() = this or s.getANormalExit() = this) and not exists(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 */
Py::Location getLocation() { result = this.getNode().getLocation() } 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
Py::Scope getScope() { Scope getScope() {
Stages::AST::ref() and Stages::AST::ref() and
if this.getNode() instanceof Py::Scope if this.getNode() instanceof 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 */
Py::Module getEnclosingModule() { result = this.getScope().getEnclosingModule() } 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(Py::Scope s) { py_scope_flow(this, s, 1) } predicate isExceptionalExit(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().(Py::Expr).getAChildNode() = result.getNode() and this.getNode().(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 Py::AstNode getNode() { result = super.getNode() } override 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 Py::Call } CallNode() { toAst(this) instanceof 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(Py::Call c | exists(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(Py::Call c | exists(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(Py::Call c, Py::Keyword k | exists(Call c, 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 Py::Call getNode() { result = super.getNode() } override 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(Py::ClassExpr cls | this.getNode() = cls.getADecoratorCall()) exists(ClassExpr cls | this.getNode() = cls.getADecoratorCall())
} }
predicate isFunctionDecoratorCall() { predicate isFunctionDecoratorCall() {
exists(Py::FunctionExpr func | this.getNode() = func.getADecoratorCall()) exists(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 Py::Attribute } AttrNode() { toAst(this) instanceof 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(Py::Attribute a | exists(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(Py::Attribute a | exists(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(Py::Attribute a | this.getNode() = a and a.getName() = result) } string getName() { exists(Attribute a | this.getNode() = a and a.getName() = result) }
override Py::Attribute getNode() { result = super.getNode() } override 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 Py::ImportMember } ImportMemberNode() { toAst(this) instanceof 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(Py::ImportMember i | this.getNode() = i and i.getModule() = result.getNode() | exists(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 Py::ImportMember getNode() { result = super.getNode() } override 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 Py::ImportExpr } ImportExprNode() { toAst(this) instanceof ImportExpr }
override Py::ImportExpr getNode() { result = super.getNode() } override 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 Py::ImportStar } ImportStarNode() { toAst(this) instanceof 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(Py::ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() | exists(ImportStar i | this.getNode() = i and i.getModuleExpr() = result.getNode() |
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
) )
} }
override Py::ImportStar getNode() { result = super.getNode() } override 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 Py::Subscript } SubscriptNode() { toAst(this) instanceof 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(Py::Subscript s | exists(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(Py::Subscript s | exists(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 Py::Subscript getNode() { result = super.getNode() } override 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 Py::Compare } CompareNode() { toAst(this) instanceof 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, Py::Cmpop op, ControlFlowNode right) { predicate operands(ControlFlowNode left, Cmpop op, ControlFlowNode right) {
exists(Py::Compare c, Py::Expr eleft, Py::Expr eright | exists(Compare c, Expr eleft, 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 Py::Compare getNode() { result = super.getNode() } override 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 Py::IfExp } IfExprNode() { toAst(this) instanceof 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 Py::IfExp getNode() { result = super.getNode() } override 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 Py::AssignExpr } AssignmentExprNode() { toAst(this) instanceof 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(Py::AssignExpr a | exists(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(Py::AssignExpr a | exists(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 Py::AssignExpr getNode() { result = super.getNode() } override 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 Py::BinaryExpr } BinaryExprNode() { toAst(this) instanceof 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 Py::BinaryExpr getNode() { result = super.getNode() } override BinaryExpr getNode() { result = super.getNode() }
ControlFlowNode getLeft() { ControlFlowNode getLeft() {
exists(Py::BinaryExpr b | exists(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(Py::BinaryExpr b | exists(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. */
Py::Operator getOp() { result = this.getNode().getOp() } 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, Py::Operator op, ControlFlowNode right) { predicate operands(ControlFlowNode left, Operator op, ControlFlowNode right) {
exists(Py::BinaryExpr b, Py::Expr eleft, Py::Expr eright | exists(BinaryExpr b, Expr eleft, 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 Py::BoolExpr } BoolExprNode() { toAst(this) instanceof 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(Py::BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and exists(BoolExpr b | this.getNode() = b and result.getNode() = b.getAValue()) and
this.getBasicBlock().dominates(result.getBasicBlock()) this.getBasicBlock().dominates(result.getBasicBlock())
} }
override Py::BoolExpr getNode() { result = super.getNode() } override 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 Py::UnaryExpr } UnaryExprNode() { toAst(this) instanceof 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 Py::UnaryExpr getNode() { result = super.getNode() } override 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(Py::Assign a | this.getNode() = a.getATarget()) exists(Assign a | a.getATarget().getAFlowNode() = this)
or or
exists(Py::AssignExpr a | this.getNode() = a.getTarget()) exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
or or
exists(Py::AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue())) exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
or or
exists(Py::Alias a | this.getNode() = a.getAsname()) exists(Alias a | a.getAsname().getAFlowNode() = this)
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(Py::Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget())) exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
or or
exists(Py::For for | this.getNode() = for.getTarget()) exists(For for | for.getTarget().getAFlowNode() = this)
or or
exists(Py::Parameter param | this.getNode() = param.asName() and exists(param.getDefault())) exists(Parameter param | this = param.asName().getAFlowNode() 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.getNode() = assigned_value(this.getNode()) and result = assigned_value(this.getNode()).getAFlowNode() 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(Py::Parameter param | this.getNode() = param.asName()) exists(Parameter param | this = param.asName().getAFlowNode())
) )
} }
} }
private Py::Expr list_or_tuple_nested_element(Py::Expr list_or_tuple) { private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
exists(Py::Expr elt | exists(Expr elt |
elt = list_or_tuple.(Py::Tuple).getAnElt() elt = list_or_tuple.(Tuple).getAnElt()
or or
elt = list_or_tuple.(Py::List).getAnElt() elt = list_or_tuple.(List).getAnElt()
| |
result = elt result = elt
or or
@@ -603,12 +603,12 @@ private Py::Expr list_or_tuple_nested_element(Py::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 `Py::Delete` such that each * There can be multiple `DeletionNode`s for each `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 Py::Delete } DeletionNode() { toAst(this) instanceof 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 Py::Tuple toAst(this) instanceof Tuple
or or
toAst(this) instanceof Py::List toAst(this) instanceof 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 Py::Tuple } TupleNode() { toAst(this) instanceof Tuple }
override ControlFlowNode getElement(int n) { override ControlFlowNode getElement(int n) {
Stages::AST::ref() and Stages::AST::ref() and
exists(Py::Tuple t | this.getNode() = t and result.getNode() = t.getElt(n)) and exists(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 Py::List } ListNode() { toAst(this) instanceof List }
override ControlFlowNode getElement(int n) { override ControlFlowNode getElement(int n) {
exists(Py::List l | this.getNode() = l and result.getNode() = l.getElt(n)) and exists(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 Py::Set } SetNode() { toAst(this) instanceof Set }
ControlFlowNode getAnElement() { ControlFlowNode getAnElement() {
exists(Py::Set s | this.getNode() = s and result.getNode() = s.getElt(_)) and exists(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 Py::Dict } DictNode() { toAst(this) instanceof 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(Py::Dict d | this.getNode() = d and result.getNode() = d.getAKey()) and exists(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(Py::Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and exists(Dict d | this.getNode() = d and result.getNode() = d.getAValue()) and
result.getBasicBlock().dominates(this.getBasicBlock()) result.getBasicBlock().dominates(this.getBasicBlock())
} }
} }
@@ -712,23 +712,21 @@ class IterableNode extends ControlFlowNode {
} }
} }
private Py::AstNode assigned_value(Py::Expr lhs) { private AstNode assigned_value(Expr lhs) {
/* lhs = result */ /* lhs = result */
exists(Py::Assign a | a.getATarget() = lhs and result = a.getValue()) exists(Assign a | a.getATarget() = lhs and result = a.getValue())
or or
/* lhs := result */ /* lhs := result */
exists(Py::AssignExpr a | a.getTarget() = lhs and result = a.getValue()) exists(AssignExpr a | a.getTarget() = lhs and result = a.getValue())
or or
/* lhs : annotation = result */ /* lhs : annotation = result */
exists(Py::AnnAssign a | a.getTarget() = lhs and result = a.getValue()) exists(AnnAssign a | a.getTarget() = lhs and result = a.getValue())
or or
/* import result as lhs */ /* import result as lhs */
exists(Py::Alias a | a.getAsname() = lhs and result = a.getValue()) exists(Alias a | a.getAsname() = lhs and result = a.getValue())
or or
/* lhs += x => result = (lhs + x) */ /* lhs += x => result = (lhs + x) */
exists(Py::AugAssign a, Py::BinaryExpr b | exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
b = a.getOperation() and result = b and lhs = b.getLeft()
)
or or
/* /*
* ..., lhs, ... = ..., result, ... * ..., lhs, ... = ..., result, ...
@@ -736,31 +734,31 @@ private Py::AstNode assigned_value(Py::Expr lhs) {
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ... * ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
*/ */
exists(Py::Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result)) exists(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.(Py::For).getTarget() = lhs result.(For).getTarget() = lhs
or or
exists(Py::Parameter param | lhs = param.asName() and result = param.getDefault()) exists(Parameter param | lhs = param.asName() and result = param.getDefault())
} }
predicate nested_sequence_assign( predicate nested_sequence_assign(
Py::Expr left_parent, Py::Expr right_parent, Py::Expr left_result, Py::Expr right_result Expr left_parent, Expr right_parent, Expr left_result, Expr right_result
) { ) {
exists(Py::Assign a | exists(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, Py::Expr left_elem, Py::Expr right_elem | exists(int i, Expr left_elem, Expr right_elem |
( (
left_elem = left_parent.(Py::Tuple).getElt(i) left_elem = left_parent.(Tuple).getElt(i)
or or
left_elem = left_parent.(Py::List).getElt(i) left_elem = left_parent.(List).getElt(i)
) and ) and
( (
right_elem = right_parent.(Py::Tuple).getElt(i) right_elem = right_parent.(Tuple).getElt(i)
or or
right_elem = right_parent.(Py::List).getElt(i) right_elem = right_parent.(List).getElt(i)
) )
| |
left_result = left_elem and right_result = right_elem left_result = left_elem and right_result = right_elem
@@ -771,9 +769,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 Py::For } ForNode() { toAst(this) instanceof For }
override Py::For getNode() { result = super.getNode() } override 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) {
@@ -784,7 +782,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(Py::For for | exists(For for |
toAst(this) = for and toAst(this) = for and
for.getIter() = result.getNode() for.getIter() = result.getNode()
| |
@@ -794,7 +792,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(Py::For for | exists(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())
@@ -811,11 +809,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 Py::Raise } RaiseStmtNode() { toAst(this) instanceof 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(Py::Raise r | exists(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())
@@ -829,36 +827,36 @@ class RaiseStmtNode extends ControlFlowNode {
*/ */
class NameNode extends ControlFlowNode { class NameNode extends ControlFlowNode {
NameNode() { NameNode() {
exists(Py::Name n | py_flow_bb_node(this, n, _, _)) exists(Name n | py_flow_bb_node(this, n, _, _))
or or
exists(Py::PlaceHolder p | py_flow_bb_node(this, p, _, _)) exists(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(Py::Variable v) { predicate defines(Variable v) {
exists(Py::Name d | this.getNode() = d and d.defines(v)) and exists(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(Py::Variable v) { exists(Py::Name d | this.getNode() = d and d.deletes(v)) } predicate deletes(Variable v) { exists(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(Py::Variable v) { predicate uses(Variable v) {
this.isLoad() and this.isLoad() and
exists(Py::Name u | this.getNode() = u and u.uses(v)) exists(Name u | this.getNode() = u and u.uses(v))
or or
exists(Py::PlaceHolder u | exists(PlaceHolder u |
this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Py::Load this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof 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().(Py::Name).getId() result = this.getNode().(Name).getId()
or or
result = this.getNode().(Py::PlaceHolder).getId() result = this.getNode().(PlaceHolder).getId()
} }
/** Whether this is a use of a local variable. */ /** Whether this is a use of a local variable. */
@@ -870,84 +868,82 @@ 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() { predicate isSelf() { exists(SsaVariable selfvar | selfvar.isSelf() and selfvar.getAUse() = this) }
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(Py::NameConstant n | py_flow_bb_node(this, n, _, _)) } NameConstantNode() { exists(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(Py::Variable v) { none() } * deprecated predicate uses(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 Py::Starred } StarredNode() { toAst(this) instanceof Starred }
ControlFlowNode getValue() { toAst(result) = toAst(this).(Py::Starred).getValue() } ControlFlowNode getValue() { toAst(result) = toAst(this).(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 Py::ExceptStmt } ExceptFlowNode() { this.getNode() instanceof ExceptStmt }
/** /**
* Gets the type handled by this exception handler. * Gets the type handled by this exception handler.
* `Py::ExceptionType` in `except Py::ExceptionType as e:` * `ExceptionType` in `except ExceptionType as e:`
*/ */
ControlFlowNode getType() { ControlFlowNode getType() {
exists(Py::ExceptStmt ex | exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and ex = this.getNode() and
result.getNode() = ex.getType() result = ex.getType().getAFlowNode()
) )
} }
/** /**
* Gets the name assigned to the handled exception, if any. * Gets the name assigned to the handled exception, if any.
* `e` in `except Py::ExceptionType as e:` * `e` in `except ExceptionType as e:`
*/ */
ControlFlowNode getName() { ControlFlowNode getName() {
exists(Py::ExceptStmt ex | exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and ex = this.getNode() and
result.getNode() = ex.getName() result = ex.getName().getAFlowNode()
) )
} }
} }
/** The ControlFlowNode for an 'except*' statement. */ /** The ControlFlowNode for an 'except*' statement. */
class ExceptGroupFlowNode extends ControlFlowNode { class ExceptGroupFlowNode extends ControlFlowNode {
ExceptGroupFlowNode() { this.getNode() instanceof Py::ExceptGroupStmt } ExceptGroupFlowNode() { this.getNode() instanceof ExceptGroupStmt }
/** /**
* Gets the type handled by this exception handler. * Gets the type handled by this exception handler.
* `Py::ExceptionType` in `except* Py::ExceptionType as e:` * `ExceptionType` in `except* ExceptionType as e:`
*/ */
ControlFlowNode getType() { ControlFlowNode getType() {
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getType() result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
} }
/** /**
* Gets the name assigned to the handled exception, if any. * Gets the name assigned to the handled exception, if any.
* `e` in `except* Py::ExceptionType as e:` * `e` in `except* ExceptionType as e:`
*/ */
ControlFlowNode getName() { ControlFlowNode getName() {
this.getBasicBlock().dominates(result.getBasicBlock()) and this.getBasicBlock().dominates(result.getBasicBlock()) and
result.getNode() = this.getNode().(Py::ExceptGroupStmt).getName() result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
} }
} }
private module Scopes { private module Scopes {
private predicate fast_local(NameNode n) { private predicate fast_local(NameNode n) {
exists(Py::FastLocalVariable v | exists(FastLocalVariable v |
n.uses(v) and n.uses(v) and
v.getScope() = n.getScope() v.getScope() = n.getScope()
) )
@@ -956,15 +952,15 @@ private module Scopes {
predicate local(NameNode n) { predicate local(NameNode n) {
fast_local(n) fast_local(n)
or or
exists(Py::SsaVariable var | exists(SsaVariable var |
var.getAUse() = n and var.getAUse() = n and
n.getScope() instanceof Py::Class and n.getScope() instanceof Class and
exists(var.getDefinition()) exists(var.getDefinition())
) )
} }
predicate non_local(NameNode n) { predicate non_local(NameNode n) {
exists(Py::FastLocalVariable flv | exists(FastLocalVariable flv |
flv.getALoad() = n.getNode() and flv.getALoad() = n.getNode() and
not flv.getScope() = n.getScope() not flv.getScope() = n.getScope()
) )
@@ -972,20 +968,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, Py::Module scope, string name) { predicate use_of_global_variable(NameNode n, Module scope, string name) {
n.isLoad() and n.isLoad() and
not non_local(n) and not non_local(n) and
not exists(Py::SsaVariable var | var.getAUse() = n | not exists(SsaVariable var | var.getAUse() = n |
var.getVariable() instanceof Py::FastLocalVariable var.getVariable() instanceof FastLocalVariable
or or
n.getScope() instanceof Py::Class and n.getScope() instanceof 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(Py::SsaVariable var) { private predicate maybe_undefined(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()
@@ -1062,13 +1058,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 Py::Scope if this.firstNode().getNode() instanceof 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 Py::Scope and not this.oneNodeBlock() if this.getLastNode().getNode() instanceof 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)
} }
@@ -1085,7 +1081,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(Py::Scope s | s.getANormalExit().getBasicBlock() = this) exists(Scope s | s.getANormalExit().getBasicBlock() = this)
or or
this.getASuccessor().reachesExit() this.getASuccessor().reachesExit()
} }
@@ -1126,7 +1122,7 @@ class BasicBlock extends @py_flow_node {
/** Gets the scope of this block */ /** Gets the scope of this block */
pragma[nomagic] pragma[nomagic]
Py::Scope getScope() { 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
@@ -1149,17 +1145,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 `Py::ConditionBlock`, if any, that controls this block and * Gets the `ConditionBlock`, if any, that controls this block and
* does not control any other `Py::ConditionBlock`s that control this block. * does not control any other `ConditionBlock`s that control this block.
* That is the `Py::ConditionBlock` that is closest dominator. * That is the `ConditionBlock` that is closest dominator.
*/ */
Py::ConditionBlock getImmediatelyControllingBlock() { 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.(Py::ConditionBlock).controls(this, _) not result.(ConditionBlock).controls(this, _)
} }
/** /**
@@ -1179,7 +1175,7 @@ private class ControlFlowNodeAlias = ControlFlowNode;
final private class FinalBasicBlock = BasicBlock; final private class FinalBasicBlock = BasicBlock;
module Cfg implements BB::CfgSig<Py::Location> { module Cfg implements BB::CfgSig<Location> {
private import codeql.controlflow.SuccessorType private import codeql.controlflow.SuccessorType
class ControlFlowNode = ControlFlowNodeAlias; class ControlFlowNode = ControlFlowNodeAlias;
@@ -1190,7 +1186,7 @@ module Cfg implements BB::CfgSig<Py::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.
Py::Location getLocation() { result = super.getNode(0).getLocation() } 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,16 +153,8 @@ 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 */
* DEPRECATED: bind a `Return` node explicitly instead, e.g. ControlFlowNode getAReturnValueFlowNode() {
* `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()
} }
deprecated override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** An import statement */ /** An import statement */

View File

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

View File

@@ -5,30 +5,24 @@ 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, ControlFlowNode c | exists(ImmutableLiteral const, Cmpop op |
c.getNode() = const and op = any(Eq eq) and branch = true
(
op = any(Eq eq) and branch = true
or
op = any(NotEq ne) and branch = false
)
|
cn.operands(c, op, node)
or or
cn.operands(node, op, c) op = any(NotEq ne) and branch = false
|
cn.operands(const.getAFlowNode(), op, node)
or
cn.operands(node, op, const.getAFlowNode())
) )
or or
exists(NameConstant const, Cmpop op, ControlFlowNode c | exists(NameConstant const, Cmpop op |
c.getNode() = const and op = any(Is is_) and branch = true
(
op = any(Is is_) and branch = true
or
op = any(IsNot isn) and branch = false
)
|
cn.operands(c, op, node)
or or
cn.operands(node, op, c) op = any(IsNot isn) and branch = false
|
cn.operands(const.getAFlowNode(), op, node)
or
cn.operands(node, op, const.getAFlowNode())
) )
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().getNode() = cls } override Node getObject() { result.asCfgNode() = cls.getAFlowNode() }
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.getNode() = any(Return ret).getValue() or node = any(Return ret).getValue().getAFlowNode() or
node.getNode() = any(Yield yield) node = any(Yield yield).getAFlowNode()
} }
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.getNode() = any(Yield yield).getValue() node = any(Yield yield).getValue().getAFlowNode()
} }
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().getNode() = yield and nodeTo.asCfgNode() = yield.getAFlowNode() and
nodeFrom.asCfgNode().getNode() = yield.getValue() and nodeFrom.asCfgNode() = yield.getValue().getAFlowNode() 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().getNode() = var.getALoad() and result.asCfgNode() = var.getALoad().getAFlowNode() and
not result.getScope() = mod not result.getScope() = mod
} }

View File

@@ -9,19 +9,7 @@ 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
@@ -338,7 +326,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
initModuleSubmoduleDefn(var.getSourceVariable(), package) and SsaSource::init_module_submodule_defn(var.getSourceVariable(), package.getEntryNode()) 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,10 +94,8 @@ 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`
exists(Return ret | result.asCfgNode() =
ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
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() { exists(DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue()) } VariableWrite() { this = v.getAStore().getAFlowNode().(DefinitionNode).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.getNode() = v.getALoad() } VariableRead() { this = v.getALoad().getAFlowNode() }
CapturedVariable getVariable() { result = v } CapturedVariable getVariable() { result = v }
} }

View File

@@ -448,7 +448,8 @@ 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().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue() retval.asCfgNode() =
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
) and ) and
edgeLabel = "return" edgeLabel = "return"
} }
@@ -470,7 +471,8 @@ 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().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue() retval.asCfgNode() =
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
) and ) and
edgeLabel = "call" edgeLabel = "call"
} }
@@ -714,10 +716,8 @@ 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()
| |
srcnode.asCfgNode().getNode() = assign.getValue() and assign.getValue().getAFlowNode() = srcnode.asCfgNode() and
exists(SequenceNode left_parent | left_parent.getNode() = assign.getATarget() | depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and
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 | left_parent.getNode() = a.getATarget().getASubExpression*()) and exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) 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
defn.getNode() = var var.getAFlowNode() = defn
| |
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
defn.getNode() = var var.getAFlowNode() = defn
| |
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
defn.getNode() = var var.getAFlowNode() = defn
| |
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() { node.getNode() = result.getADecorator() } override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
} }
} }
@@ -73,10 +73,7 @@ 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() {
exists(Return ret | this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode()
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,10 +2872,7 @@ module PrivateDjango {
DataFlow::CfgNode DataFlow::CfgNode
{ {
DjangoRedirectViewGetRedirectUrlReturn() { DjangoRedirectViewGetRedirectUrlReturn() {
exists(Return ret | node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
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() { node.getNode() = result.getADecorator() } override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
override string getFramework() { result = "FastAPI" } override string getFramework() { result = "FastAPI" }
@@ -309,10 +309,7 @@ module FastApi {
FastApiRouteSetup routeSetup; FastApiRouteSetup routeSetup;
FastApiRequestHandlerReturn() { FastApiRequestHandlerReturn() {
exists(Return ret | node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
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() { node.getNode() = result.getADecorator() } override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
} }
/** /**
@@ -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
exists(Return ret | ret.getScope() = routeHandler and node.getNode() = ret.getValue()) and node = routeHandler.getAReturnValueFlowNode() 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() { node.getNode() = result.getADecorator() } override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
} }
/** /**
@@ -71,7 +71,7 @@ private module FlaskAdmin {
override Function getARequestHandler() { override Function getARequestHandler() {
exists(Flask::FlaskViewClass cls | exists(Flask::FlaskViewClass cls |
node.getNode() = cls.getADecorator() and cls.getADecorator().getAFlowNode() = node and
result = cls.getARequestHandler() result = cls.getARequestHandler()
) )
} }

View File

@@ -166,10 +166,7 @@ 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() {
exists(Return ret | this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and
ret.getScope() = any(View::ViewCallable vc) and
this.asCfgNode().getNode() = ret.getValue()
) and
not this = instance() not this = instance()
} }

View File

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

View File

@@ -182,10 +182,7 @@ private module Twisted {
DataFlow::CfgNode DataFlow::CfgNode
{ {
TwistedResourceRenderMethodReturn() { TwistedResourceRenderMethodReturn() {
exists(Return ret | this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode()
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(PyFlow::ControlFlowNode cfg, AstExtended::AstNode n | cfg.getNode() = n) exists(any(AstExtended::AstNode n).getAFlowNode())
or or
exists(any(PyFlow::BasicBlock b).getImmediateDominator()) exists(any(PyFlow::BasicBlock b).getImmediateDominator())
or or

View File

@@ -56,9 +56,8 @@ 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, ControlFlowNode exprCfg | exists(CallableExpr expr |
exprCfg.getNode() = expr and this = TPythonFunctionObject(expr.getAFlowNode()) and
this = TPythonFunctionObject(exprCfg) and
result = expr.getInnerScope() result = expr.getInnerScope()
) )
} }
@@ -81,12 +80,11 @@ 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, Return ret, ControlFlowNode rval, ControlFlowNode forigin | exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
func = this.getScope() and func = this.getScope() and
callee.appliesToScope(func) callee.appliesToScope(func)
| |
ret.getScope() = func and rval = func.getAReturnValueFlowNode() 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)
) )
@@ -162,11 +160,10 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
} }
private BasicBlock blockReturningNone(Function func) { private BasicBlock blockReturningNone(Function func) {
exists(Return ret, ControlFlowNode ret_ | exists(Return ret |
not exists(ret.getValue()) and not exists(ret.getValue()) and
ret.getScope() = func and ret.getScope() = func and
ret_.getNode() = ret and result = ret.getAFlowNode().getBasicBlock()
result = ret_.getBasicBlock()
) )
} }

View File

@@ -113,9 +113,8 @@ 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, ControlFlowNode exprCfg | exists(ClassExpr expr |
exprCfg.getNode() = expr and this = TPythonClassObject(expr.getAFlowNode()) and
this = TPythonClassObject(exprCfg) and
result = expr.getInnerScope() result = expr.getInnerScope()
) )
} }

View File

@@ -745,12 +745,7 @@ 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() { ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
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.getNode() = f.getADecorator() | exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() |
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 |
f.getNode() = i and i.getAFlowNode() = f 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,9 +2118,8 @@ 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, ControlFlowNode baseNode | exists(ObjectInternal base |
baseNode.getNode() = pycls.getBase(n) and PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _)
PointsToInternal::pointsTo(baseNode, _, base, _)
| |
result = base and base != ObjectInternal::unknown() result = base and base != ObjectInternal::unknown()
or or
@@ -2224,10 +2223,7 @@ module Types {
} }
private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) { private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) {
exists(CallNode deco | result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction()
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) {
@@ -2266,7 +2262,7 @@ module Types {
} }
private EssaVariable metaclass_var(Class cls) { private EssaVariable metaclass_var(Class cls) {
result.getASourceUse().getNode() = cls.getMetaClass() result.getASourceUse() = cls.getMetaClass().getAFlowNode()
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.getNode() = this.getPyClass().getMetaClass() } ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() }
/** 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,9 +195,8 @@ 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, ControlFlowNode origin_ | exists(ControlFlowNodeWithPointsTo use, Expr origin |
origin_.getNode() = origin and use.refersTo(result, this, origin.getAFlowNode())
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.getNode() = t.getAnElt()) exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode())
} }
/** /**

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
iterable.getNode() = f.getIter() and f.getIter().getAFlowNode() = iterable and
this.(ControlFlowNode).getNode() = f.getTarget() and f.getTarget().getAFlowNode() = this 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,10 +137,7 @@ 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() {
exists(Return ret | result = this.getFunction().getAReturnValueFlowNode()
ret.getScope() = this.getFunction() and
result.getNode() = ret.getValue()
)
} }
override string descriptiveString() { override string descriptiveString() {
@@ -173,7 +170,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) |
pvar.getDefinition().getNode() = p.asName() p.asName().getAFlowNode() = pvar.getDefinition()
) 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 | this.(ControlFlowNode).getNode() = func.getVararg()) exists(Function func | func.getVararg().getAFlowNode() = this)
} }
} }
@@ -352,9 +352,7 @@ module TupleObject {
} }
class NonEmptyTupleObject extends TupleObject { class NonEmptyTupleObject extends TupleObject {
NonEmptyTupleObject() { NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) }
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
}
override boolean booleanValue() { result = true } override boolean booleanValue() { result = true }
} }

View File

@@ -48,11 +48,9 @@ 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, ControlFlowNode subObjCfg, ControlFlowNode selfDictCfg | exists(SsaVariable v |
subObjCfg.getNode() = sub.getObject() and selfDictCfg.getNode() = self_dict v.getAUse() = sub.getObject().getAFlowNode() and
| 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
@@ -64,10 +62,9 @@ class CheckClass extends ClassObject {
pragma[nomagic] pragma[nomagic]
private predicate monkeyPatched(string name) { private predicate monkeyPatched(string name) {
exists(Attribute a, ControlFlowNode objCfg | exists(Attribute a |
objCfg.getNode() = a.getObject() and
a.getCtx() instanceof Store and a.getCtx() instanceof Store and
PointsTo::points_to(objCfg, _, this, _, _) and PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and
a.getName() = name a.getName() = name
) )
} }
@@ -87,9 +84,9 @@ class CheckClass extends ClassObject {
} }
predicate interestingUndefined(SelfAttributeRead a) { predicate interestingUndefined(SelfAttributeRead a) {
exists(string name, ControlFlowNode aCfg | name = a.getName() and aCfg.getNode() = a | exists(string name | name = a.getName() |
this.interestingContext(a, name) and this.interestingContext(a, name) and
not this.definedInBlock(aCfg.getBasicBlock(), name) not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
) )
} }
@@ -112,9 +109,8 @@ 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, ControlFlowNode saCfg | exists(SelfAttributeStore sa |
saCfg.getNode() = sa and sa.getAFlowNode().getBasicBlock() = b and
saCfg.getBasicBlock() = b and
sa.getName() = name and sa.getName() = name and
sa.getClass() = this.getPyClass() sa.getClass() = this.getPyClass()
) )

View File

@@ -15,9 +15,7 @@
import python import python
import semmle.python.ApiGraphs import semmle.python.ApiGraphs
predicate doesnt_reraise(ExceptStmt ex) { predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() }
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
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call_objectapi(func)) and call.getAFlowNode() = 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
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(func)) and call.getAFlowNode() = get_a_call(func) and
not get_function_or_initializer(func).isLegalArgumentName(name) not get_function_or_initializer(func).isLegalArgumentName(name)
} }
@@ -146,9 +146,7 @@ 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
exists(ControlFlowNode callCfg | callCfg.getNode() = call | call.getAFlowNode() = get_a_call_objectapi(callable) and
callCfg = get_a_call_objectapi(callable)
) and
limit = func.minParameters() - 1 limit = func.minParameters() - 1
) )
} }
@@ -174,7 +172,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
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and call.getAFlowNode() = get_a_call(callable) and
limit = func.minParameters() - 1 limit = func.minParameters() - 1
) )
} }
@@ -193,9 +191,7 @@ 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
exists(ControlFlowNode callCfg | callCfg.getNode() = call | call.getAFlowNode() = get_a_call_objectapi(callable) and
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
@@ -215,7 +211,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
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and call.getAFlowNode() = 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,15 +36,11 @@ 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 |
b.getNode(i1).getNode() = k1 and k1.getAFlowNode() = b.getNode(i1) and
b.getNode(i2).getNode() = k2 and k2.getAFlowNode() = b.getNode(i2) and
i1 < i2 i1 < i2
) )
or or
exists(ControlFlowNode k1Cfg, ControlFlowNode k2Cfg | k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
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,18 +98,16 @@ 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, ControlFlowNode fmtCfg | exists(CallNode call | call = format_expr.getAFlowNode() |
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(_, fmtCfg) and call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmt.getAFlowNode()) 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(_, fmtCfg) and .pointsTo(_, fmt.getAFlowNode()) 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.getNode() = comp | exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
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.getNode() = comp | exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
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.getNode() = div and bin = div.getAFlowNode() 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,9 +19,7 @@ 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, ControlFlowNode rvCfg | rvCfg.getNode() = rv | not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
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,12 +69,7 @@ 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 = unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
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() |
v.getNode() = ret.getValue().getAUse() and ret.getValue().getAFlowNode() = v.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() |
callee.getNode() = ret.getValue().getACall() and ret.getValue().getAFlowNode() = callee.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 this.asCfgNode().getNode() = k.getValue()) exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this.asCfgNode())
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
iter.getNode() = loop.getIter() and loop.getIter().getAFlowNode() = iter 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) {
s.getDefinition().getNode() = f.getTarget() and v = s.getVariable() f.getTarget().getAFlowNode() = s.getDefinition() 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
iter.getNode() = loop.getIter() and loop.getIter().getAFlowNode() = iter 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,13 +24,11 @@ predicate func_with_side_effects(Expr e) {
} }
predicate call_with_side_effect(Call e) { predicate call_with_side_effect(Call e) {
exists(ControlFlowNode eCfg | eCfg.getNode() = e | e.getAFlowNode() =
eCfg = API::moduleImport("subprocess")
API::moduleImport("subprocess") .getMember(["call", "check_call", "check_output"])
.getMember(["call", "check_call", "check_output"]) .getACall()
.getACall() .asCfgNode()
.asCfgNode()
)
} }
predicate probable_side_effect(Expr e) { predicate probable_side_effect(Expr e) {

View File

@@ -133,11 +133,7 @@ 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
exists(ControlFlowNode thisCfg, ControlFlowNode resultCfg | this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and
thisCfg.getNode() = this and resultCfg.getNode() = result
|
thisCfg.strictlyReaches(resultCfg)
) and
result.isUse() result.isUse()
} }

View File

@@ -13,21 +13,18 @@
import python import python
import Definition import Definition
from from ListComprehensionDeclaration l, Name use, Name defn
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
lCfg.getNode() = l and l.getAFlowNode().strictlyReaches(use.getAFlowNode()) 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 useCfg.strictlyReaches(lCfg) and not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) 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() = useCfg and v.getAUse() = use.getAFlowNode() and
not v.getDefinition().strictlyDominates(lCfg) not v.getDefinition().strictlyDominates(l.getAFlowNode())
) )
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,11 +26,8 @@ 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, ControlFlowNode loopCfg, ControlFlowNode useCfg | exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) |
loop = loop_probably_defines(use.getVariable()) and loop.getAFlowNode().strictlyReaches(use.getAFlowNode())
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.getNode() = asgn1 and def = asgn1.getAFlowNode() and
redef.getNode() = asgn2 redef = asgn2.getAFlowNode()
| |
def.isUnused() and def.isUnused() and
def.getARedef() = redef and def.getARedef() = redef and

View File

@@ -88,9 +88,7 @@ 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, ControlFlowNode cCfg | exists(Comp c | c.getFunction().getStmt(0) = f | c.getAFlowNode().getAPredecessor() = result)
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,10 +19,9 @@ 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, ControlFlowNode sCfg | exists(Stmt s |
loop_probably_executes_at_least_once(s) and loop_probably_executes_at_least_once(s) and
sCfg.getNode() = s and s.getAFlowNode().getBasicBlock() = loop
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
exists(ControlFlowNode uCfg | uCfg.getNode() = u | controlled.contains(uCfg)) controlled.contains(u.getAFlowNode())
) )
} }
@@ -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, ControlFlowNode useCfg | useCfg.getNode() = use | exists(GlobalVariable v, BasicBlock b, int i |
i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = useCfg i = min(int j | b.getNode(j).getNode() = v.getALoad()) and b.getNode(i) = use.getAFlowNode()
) )
} }
predicate first_undefined_use(Name use) { predicate first_undefined_use(Name use) {
undefined_use(use) and undefined_use(use) and
exists(GlobalVariable v, ControlFlowNode useCfg | v.getALoad() = use and useCfg.getNode() = use | exists(GlobalVariable v | v.getALoad() = 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(useCfg.getBasicBlock()) other.getBasicBlock().strictlyDominates(use.getAFlowNode().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, ControlFlowNode useCfg | exists(SsaVariableWithPointsTo l, Function f |
f = use.getScope() and useCfg.getNode() = use and l.getAUse() = useCfg f = use.getScope() and l.getAUse() = use.getAFlowNode()
| |
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
exists(ControlFlowNode uCfg | uCfg.getNode() = u | defn.strictlyReaches(uCfg)) defn.strictlyReaches(u.getAFlowNode())
or or
// indirectly // indirectly
defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope() defn.getBasicBlock().reachesExit() and u.getScope() != unused.getScope()

View File

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

View File

@@ -80,11 +80,10 @@ class VersionGuard extends ConditionBlock {
VersionGuard() { this.getLastNode() instanceof VersionTest } VersionGuard() { this.getLastNode() instanceof VersionTest }
} }
from ImportExpr ie, ControlFlowNode ieCfg from ImportExpr ie
where where
ieCfg.getNode() = ie and
not ie.(ExprWithPointsTo).refersTo(_) and not ie.(ExprWithPointsTo).refersTo(_) and
exists(Context c | c.appliesTo(ieCfg)) and exists(Context c | c.appliesTo(ie.getAFlowNode())) and
not ok_to_fail(ie) and not ok_to_fail(ie) and
not exists(VersionGuard guard | guard.controls(ieCfg.getBasicBlock(), _)) not exists(VersionGuard guard | guard.controls(ie.getAFlowNode().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.getNode() = e | not PointsTo::pointsTo(f, _, _, _)) exists(ControlFlowNode f | f = e.getAFlowNode() | 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, ControlFlowNode eCfg | eCfg.getNode() = e and ssa.getAUse() = eCfg | not exists(SsaVariable ssa | ssa.getAUse() = e.getAFlowNode() |
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.getNode() = e | not f.refersTo(_)) where exists(ControlFlowNodeWithPointsTo f | f = e.getAFlowNode() | 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 | this.asCfgNode().getNode() = a.getTarget()) exists(AugAssign a | a.getTarget().getAFlowNode() = this.asCfgNode())
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,7 +5,5 @@
import python import python
select count(Comprehension c | select count(Comprehension c |
count(c.toString()) != 1 or count(c.toString()) != 1 or count(c.getLocation()) != 1 or not exists(c.getAFlowNode())
count(c.getLocation()) != 1 or
not exists(ControlFlowNode n | n.getNode() = c)
) )

View File

@@ -45,15 +45,13 @@ private class VersionGuardedNode extends DataFlow::Node {
VersionGuardedNode() { VersionGuardedNode() {
version in [2, 3] and version in [2, 3] and
exists(If parent, CompareNode c, ControlFlowNode litCfg | exists(If parent, CompareNode c | parent.getBody().contains(this.asExpr()) |
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), litCfg) .asCfgNode(), any(Eq eq),
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.getNode() = def and d = def.getAFlowNode() 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(ControlFlowNode n | n.getNode() = a) select a.getLocation().getStartLine(), s.getName(), a, count(a.getAFlowNode())

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 | f.getNode() = s.getValue()) and exists(ExprStmt s | s.getValue().getAFlowNode() = f) 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 | f.getNode() = s.getValue()) and exists(ExprStmt s | s.getValue().getAFlowNode() = f) 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().getNode() = arg n.(DataFlow::CfgNode).getNode() = arg.getAFlowNode()
)) ))
then has_taint = true then has_taint = true
else has_taint = false else has_taint = false

View File

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

View File

@@ -1,12 +0,0 @@
| 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

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

View File

@@ -1,13 +1,9 @@
#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
@@ -15,8 +11,4 @@ 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); // $ Alert[rust/summary/cryptographic-operations] _ = md5_alt::compute(harmless);
_ = md5_alt::compute(credit_card_no); // $ Alert[rust/summary/cryptographic-operations] Alert[rust/weak-sensitive-data-hashing] _ = md5_alt::compute(credit_card_no); // $ Alert[rust/weak-sensitive-data-hashing]
_ = md5_alt::compute(password); // $ Alert[rust/summary/cryptographic-operations] Alert[rust/weak-sensitive-data-hashing] _ = md5_alt::compute(password); // $ Alert[rust/weak-sensitive-data-hashing]
_ = md5_alt::compute(encrypted_password); // $ Alert[rust/summary/cryptographic-operations] _ = md5_alt::compute(encrypted_password);
// 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(); // $ Alert[rust/summary/cryptographic-operations] let mut md5_hasher = md5::Md5::new();
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(); // $ Alert[rust/summary/cryptographic-operations] _ = md5::Md5::new().chain_update(harmless).chain_update(harmless).chain_update(harmless).finalize();
_ = 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().chain_update(harmless).chain_update(password).chain_update(harmless).finalize(); // $ 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(); // $ Alert[rust/summary/cryptographic-operations] let mut md5_hasher = md5::Md5::new();
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,75 +153,8 @@ 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(); // $ Alert[rust/summary/cryptographic-operations] let mut md5_hasher = md5::Md5::new();
_ = 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
}

View File

@@ -224,13 +224,6 @@ signature module AstSig<LocationSig Location> {
*/ */
default AstNode getTryElse(TryStmt try) { none() } default AstNode getTryElse(TryStmt try) { none() }
/**
* Gets the `else` block of loop statement `loop`, if any.
*
* Only some languages (e.g. Python) support `for-else` constructs.
*/
default AstNode getLoopElse(LoopStmt loop) { none() }
/** A catch clause in a try statement. */ /** A catch clause in a try statement. */
class CatchClause extends AstNode { class CatchClause extends AstNode {
/** Gets the variable declared by this catch clause. */ /** Gets the variable declared by this catch clause. */
@@ -1585,32 +1578,19 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
n2.isBefore(loopstmt.getBody()) n2.isBefore(loopstmt.getBody())
or or
n1.isAfterValue(cond, any(BooleanSuccessor b | b.getValue() = while.booleanNot())) and n1.isAfterValue(cond, any(BooleanSuccessor b | b.getValue() = while.booleanNot())) and
( n2.isAfter(loopstmt)
n2.isBefore(getLoopElse(loopstmt))
or
not exists(getLoopElse(loopstmt)) and n2.isAfter(loopstmt)
)
or or
n1.isAfter(loopstmt.getBody()) and n1.isAfter(loopstmt.getBody()) and
n2.isAdditional(loopstmt, loopHeaderTag()) n2.isAdditional(loopstmt, loopHeaderTag())
) )
or or
exists(LoopStmt loopstmt |
n1.isAfter(getLoopElse(loopstmt)) and
n2.isAfter(loopstmt)
)
or
exists(ForeachStmt foreachstmt | exists(ForeachStmt foreachstmt |
n1.isBefore(foreachstmt) and n1.isBefore(foreachstmt) and
n2.isBefore(foreachstmt.getCollection()) n2.isBefore(foreachstmt.getCollection())
or or
n1.isAfterValue(foreachstmt.getCollection(), n1.isAfterValue(foreachstmt.getCollection(),
any(EmptinessSuccessor t | t.getValue() = true)) and any(EmptinessSuccessor t | t.getValue() = true)) and
( n2.isAfter(foreachstmt)
n2.isBefore(getLoopElse(foreachstmt))
or
not exists(getLoopElse(foreachstmt)) and n2.isAfter(foreachstmt)
)
or or
n1.isAfterValue(foreachstmt.getCollection(), n1.isAfterValue(foreachstmt.getCollection(),
any(EmptinessSuccessor t | t.getValue() = false)) and any(EmptinessSuccessor t | t.getValue() = false)) and
@@ -1623,11 +1603,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
n2.isAdditional(foreachstmt, loopHeaderTag()) n2.isAdditional(foreachstmt, loopHeaderTag())
or or
n1.isAdditional(foreachstmt, loopHeaderTag()) and n1.isAdditional(foreachstmt, loopHeaderTag()) and
( n2.isAfter(foreachstmt)
n2.isBefore(getLoopElse(foreachstmt))
or
not exists(getLoopElse(foreachstmt)) and n2.isAfter(foreachstmt)
)
or or
n1.isAdditional(foreachstmt, loopHeaderTag()) and n1.isAdditional(foreachstmt, loopHeaderTag()) and
n2.isBefore(foreachstmt.getVariable()) n2.isBefore(foreachstmt.getVariable())